Cài đặt sự thừa kếSử dụng các thành phần của lớp cơ sở Định nghĩa lại các hàm thành phần Truyền thông tin giữa các hàm thiết lập của lớp dẫn xuất và lớp cơ sở Các loại dẫn xuất khác
Trang 1Cài đặt sự thừa kế
Sử dụng các thành phần của lớp cơ sở
Định nghĩa lại các hàm thành phần
Truyền thông tin giữa các hàm thiết lập của lớp dẫn xuất và lớp cơ sở
Các loại dẫn xuất khác nhau và sự thay đổi trạng thái của các thành phần lớp
cơ sở
Sự tương thích giữa các đối tượng của lớp dẫn xuất và lớp cơ sở
Toán từ gán và thừa kế
Hàm ảo và tính đa hình
Hàm ảo thuần tuý và lớp cơ sở trừu tượng
Đa thừa kế và các vấn đề liên quan
Trang 21 GIỚI THIỆU
- Thừa kế cho phép ta định nghĩa một lớp mới, gọi là lớp dẫn xuất, từ một lớp đã có, gọi là lớp cơ
sở Lớp dẫn xuất sẽ thừa kế các thành phần (dữ liệu, hàm) của lớp cơ sở, đồng thời thêm các thành phần mới
- Thừa kế cho phép không cần phải biên dịch lại các thành phần chương trình vốn đã có trong các lớp cơ sở và hơn thế nữa không cần phải có chương trình nguồn tương ứng
Trang 3chênh lệch giá mua bán
Lớp: xe gắn máy thừa kế từ lớp mặt hàng nhập khẩu
Trang 5Lớ p dẫn xuất chỉ thừa kế từ một lớp cơ cở
point(float ox, float oy) {x = ox; y = oy; }
point(point &p) {x = p.x; y = p.y;}
coloredpoint(float ox, float oy, unsigned int c):point(ox,oy) {
color = c;
} coloredpoint(coloredpoint &b):point((point &)b) {
color = b.color; } void display() {
cout<<"Ham coloredpoint::display()\n";
point::display();//
cout<<"Mau "<<color<<endl;
} };
Trang 6Lớp dẫn xuất chỉ thừa kế từ một lớp cơ cở
Trang 7- Các thành phần private trong lớp cơ sở không thể truy nhập được từ các lớp dẫn xuất
- Lớp dẫn xuất có thể truy nhập đến thành phần protected và public của lớp cơ sở
- Phạm vi của lớp dẫn xuất che lấp lớp cơ sở, do đó ta cần dùng toán tử phạm vi :: khi truy xuất đến thành phần thuộc lớp cơ sở mà có đã được kế thừa lại trong lớp dẫn xuất
- Một đối tượng của lớp dẫn xuất có thể thay thế một đối tượng của lớp cơ sở -> có việc chuyển kiểu ngầm định từ một đối tượng thuộc lớp dẫn xuất sang một đối tượng thuộc lớp cơ sở
Trang 8point(float ox, float oy) {x = ox; y = oy; }
point(point &p) {x = p.x; y = p.y;}
} coloredpoint(float ox, float oy, unsigned int c):point(ox,oy) {
color = c;
} coloredpoint(coloredpoint &b):point((point
&)b) { color = b.color;
} void display() { cout<<"Ham coloredpoint::display()\n"; point::display();/*gäi tíi hµm cïng tªn trong líp c¬ së*/
cout<<"Mau "<<color<<endl;
} };
Trang 10T ương thích giữa con trỏ lớp dẫn xuất và con trỏ lớp cơ sở
point(float ox, float oy) {x = ox; y = oy; }
point(point &p) {x = p.x; y = p.y;}
coloredpoint(float ox, float oy, unsigned int c):point(ox,oy) {
color = c;
} coloredpoint(coloredpoint &b):point((point
&)b) { color = b.color;
} void display() { cout<<"Ham coloredpoint::display()\n"; point::display();
cout<<"Mau "<<color<<endl;
} };
Trang 11T ương thích giữa con trỏ lớp dẫn xuất và con trỏ lớp cơ sở
Câu lệnh adp->display(); gọi tới hàm point::display();
Trang 12T ương thích giữa tham chiếu lớp dẫn xuất và tham chiếu lớp cơ sở
point(float ox, float oy) {x = ox; y = oy; }
point(point &p) {x = p.x; y = p.y;}
coloredpoint(float ox, float oy, unsigned int c):point(ox,oy) {
color = c; } coloredpoint(coloredpoint &b):point((point
&)b) { color = b.color;
} void display() { cout<<"Ham coloredpoint::display()\n"; point::display();
cout<<"Mau "<<color<<endl;
} };
Trang 13T ương thích giữa tham chiếu lớp dẫn xuất và tham chiếu lớp cơ sở
Câu lệnh adp->display(); gọi tới hàm point::display();
Trang 14Hàm thiết lập trong lớp dẫn xuất
- Một đối tượng trong lớp dẫn xuất có thể coi là một đối tượng thuộc lớp cơ sở, việc gọi hàm thiết lập trong lớp dẫn xuất sẽ kéo theo việc gọi đến một hàm thiết lập trong lớp cơ sở
- Thứ tự thực hiện của hàm thiết lập: hàm thiết lập của lớp cơ sở, hàm thiết lập của lớp dẫn xuất
Trang 15float getx() {return x;}
float gety() {return y;}
point() {x = 0; y = 0;}
point(float ox, float oy) {x = ox; y = oy; }
point(point &p) {x = p.x; y = p.y;}
} coloredpoint(float ox, float oy, unsigned int c); coloredpoint(coloredpoint
&b):point(b.getx(),b.gety()) { cout<<"Goi ham thiet lap sao chep""
<<" coloredpoint::coloredpoint(coloredpoint
&) \n";
color = b.color;
} void display() { cout<<"Ham coloredpoint::display()\n";
point::display();
cout<<"Mau "<<color<<endl;
} };
Trang 17Các kiểu dẫn xuất khác nhau
Trang 18Dẫn xuất public
- Trong dẫn xuất public các thành phần các hàm bạn và các đối tượng của lớp dẫn xuất không thể truy xuất đến thành phần private của lớp cơ sở
- Các thành phần protected trong lớp cơ sở trở thành các thành phần private trong lớp dẫn xuất
- Các thành phần public của lớp cơ sở vẫn là public trong lớp dẫn xuất
Dẫn xuất private
- Các thành phần public trong lớp cơ sở trở thành các thành phần private trong lớp dẫn xuất
- Các thành phần protected trong lớp cơ sở có thể truy nhập được từ các hàm thành phần và các hàm bạn của lớp dẫn xuất
Dẫn xuất protected
- Các thành phần public, protected trong lớp cơ sở trở thành các thành phần protected trong lớp dẫn xuất
Trang 19Líp c¬ së DÉn xuÊt public DÉn xuÊt
protected DÉn xuÊt
private
FMA NSD TN TTM NSD TN TTM NSD TN TTM NSD TN
Tõ viÕt t¾t DiÔn giải
Trang 20- Xây dựng sơ đồ lớp theo đặc tả sau:
Một trường đại học có nhiều cá nhân gồm nhân viên, sinh viên Nhân viên gồm nhân viên quản lý và giảng viên giảng dạy Sinh viên chia làm nhiều loại như học viên cao học, sinh viên chính qui, sinh viên cao đẵng, tại chức Trong trường cũng có nhiều phòng ban và các khoa Đối với nhân viên và cán bộ giảng dạy trường cần quản lý thông tin như mã nhân viên, tên, năm sinh, địa chỉ, hệ số lượng, phòng hay khoa làm việc Đối với giảng viên cần quản lý thêm thông tin về học hàm, học vị, môn giảng dạy Đối với nhân viên cần quản lý thông tin về chuyên môn Đối với khoa hay phòng ban thì quản lý tên, tên trưởng khoa/phòng, số sinh viên theo học từng loại nếu là khoa
Đối với học viên và sinh viên cần quản lý mã số, tên, năm sinh, khóa, thuộc khoa nào, địa chỉ Đối với học viên cao học cần quản lý thông tin chuyên ngành học đại học Đối với học viên tại chức cần quản lý cơ quan công tác
Trang 213.1 Giới thiệu
- Một tham trỏ tới đối tượng có thể nhận địa chỉ của bất kỳ đối tượng con cháu nào Tuy nhiên, lời gọi tới một phương thức của một đối tượng được trỏ tới luôn được coi như lời gọi đến phương thức tương ứng với kiểu con trỏ, không phải tương ứng với đối tượng đang trỏ đến Trường hợp này gọi là “gán kiểu tĩnh – static typing” hay “gán kiểu sớm – early binding”
- Để gọi phương thức tương ứng với đối tượng được trỏ đến, cần phải xác định kiểu của đối tượng tại thời điểm thực hiện chương trình Trường hợp này ta gọi là “ gán kiểu động – dynamic typing” hay gán kiểu muộn – late binding”
- C++ đưa ra khái niệm hàm ảo (virtural) để giải quyết trương hợp này
Trang 22void move(float dx, float dy) {
x += dx;
y += dy;
} };
class coloredpoint : public point { unsigned int color;
public:
coloredpoint():point() { cout<<"coloredpoint::coloredpoint()\n"; color =0;
}
Trang 23coloredpoint(float ox, float oy, unsigned int c);
coloredpoint(coloredpoint &b):point((point &)b) {
coloredpoint::coloredpoint(float ox, float oy,
unsigned c) : point(ox, oy){
cout<<"coloredpoint pc(2,3,5);\n"; coloredpoint pc(2,3,5);
Trang 24- Từ khoá virtual có thể đặt trước hay sau tên kiểu dữ liệu nhưng phải trước tên hàm để chỉ rằng là một hàm ảo
- Tuỳ thuộc vào kiểu của đối tượng có địa chỉ chứa trong con trỏ lớp dẫn xuất ptr mà lời gọi hàm
ptr->display() sẽ gọi đến phương thức display() của point hay coloredpoint
Trang 25x += dx;
y += dy;
} virtual void Identifier() { cout<<"Diem khong mau \n";
} };
void point::display() { cout<<"Toa do : "<<x<<" "<<y<<endl; Identifier();
}
Trang 26class coloredpoint : public point {
unsigned int color;
coloredpoint(float ox, float oy, unsigned int c);
coloredpoint(coloredpoint &b):point((point &)b) {
cout<<"coloredpoint::coloredpoint(float, float, unsigned)\n";
color = c;
}void main() { clrscr();
Trang 273.2 Phạm vi của khai báo virtual: một hàm f được khai báo virtual trong lớp A, nó được xem như thể
hiện của sự ghép kiểu động trong lớp A và trong tất cả các lớp dẫn xuất từ A
x = p.x;
y = p.y;
} void display() ; void move(float dx, float dy) {
x += dx;
y += dy;
} virtual void Identifier() { cout<<"Diem khong mau \n";
} };
Trang 283.2 Phạm vi của khai báo virtual: một hàm f được khai báo virtual trong lớp A, nó được xem như thể
hiện của sự ghép kiểu động trong lớp A và trong tất cả các lớp dẫn xuất từ A
void point::display() {
cout<<"Toa do : "<<x<<" "<<y<<endl;
Identifier();
}
class coloredpoint : public point {
unsigned int color;
coloredpoint(float ox, float oy, unsigned int c);
coloredpoint(coloredpoint &b):point((point &)b) {
} };
Trang 293.2 Phạm vi của khai báo virtual: một hàm f được khai báo virtual trong lớp A, nó được xem như thể
hiện của sự ghép kiểu động trong lớp A và trong tất cả các lớp dẫn xuất từ A
class threedimpoint : public point {
coloredthreedimpoint(float ox, float oy, float oz,unsigned c):
threedimpoint (ox, oy, oz) {
color = c; } coloredthreedimpoint(coloredthreedimpoint &p) :threedimpoint(p) {
color = p.color;
} void Identifier() { cout<<"Diem mau : "<<color<<endl;
} };
Trang 303.2 Phạm vi của khai báo virtual: một hàm f được khai báo virtual trong lớp A, nó được xem như thể
hiện của sự ghép kiểu động trong lớp A và trong tất cả các lớp dẫn xuất từ A
coloredpoint::coloredpoint(float ox, float oy, unsigned
cout<<"p3dc.display();\n";
p3dc.display();//goi coloredthreedimpoint::Identifier()
getch();
}
Trang 31Không nhất thiết phải định nghĩa lại hàm ảo
void move(float dx, float dy) {
x += dx;
y += dy;
} };
Trang 32Không nhất thiết phải định nghĩa lại hàm ảo
class coloredpoint : public point {
unsigned int color;
coloredpoint(float ox, float oy, unsigned int c);
coloredpoint(coloredpoint &b):point((point &)b) {
cout<<"coloredpoint pc(2,3,5);\n"; coloredpoint pc(2,3,5);
Trang 33Khai báo hàm ảo ở một lớp bất kỳ trong sơ đồ thừa kế
x += dx;
y += dy;
} void Identifier() { cout<<"Diem khong mau \n";
} };
void point::display() { cout<<"Toa do : "<<x<<" "<<y<<endl; Identifier();
}
Trang 34Khai báo hàm ảo ở một lớp bất kỳ trong sơ đồ thừa kế
class threedimpoint : public point {
} coloredthreedimpoint(float ox, float oy, float oz,unsigned c):
threedimpoint (ox, oy, oz) {
color = c;
} coloredthreedimpoint(coloredthreedimpoint
&p) :threedimpoint(p) { color = p.color;
} void Identifier() { cout<<"Diem mau : "<<color<<endl;
} };
Trang 35Khai báo hàm ảo ở một lớp bất kỳ trong sơ đồ thừa kế
Trang 36x = p.x;
y = p.y;
} virtual ~point() { cout<<"point::~point() \n"; }
void display() ; void move(float dx, float dy) {
x += dx;
y += dy;
} virtual void Identifier() { cout<<"Diem khong mau \n"; }
};
Trang 37} void Identifier() { cout<<"Toa do z : "<<z<<endl;
} };
class coloredthreedimpoint : public threedimpoint { unsigned color;
public:
coloredthreedimpoint() { color = 0;
}
Trang 38point *p0 = new point(2,10);
point *p1 = new threedimpoint(2,3,5);
point *p2 = new coloredthreedimpoint(2,3,4,10); delete p0;
delete p1;
delete p2;
getch();
}
Trang 394.1 Đặt vấn đề
Đa thừa kế cho phép một lớp có thể là dẫn xuất của nhiều lớp cơ sở, do vậy những gì đã đề cập trong phần đơn thừa kế được tổng quát hoá cho trường hợp đa thừa kế Tuy vậy cần phải giải quyết các vấn đề sau:
Làm thế nào biểu thị được tính độc lập của các thành phần cùng tên bên trong một lớp dẫn xuất?
Các hàm thiết lập và huỷ bỏ được gọi như thế nào: thứ tự, truyền thông tin v.v.?
Làm thế nào giải quyết tình trạng thừa kế xung đột trong đó, lớp D dẫn xuất từ B và C, và cả hai cùng là dẫn xuất của A.
Trang 404.1 Đặt vấn đề
Ví dụ:
coloredpoint
col point
col ( ) { }
~color() { }void display(){ } };
Trang 414.1 Đặt vấn đề
Ví dụ:
Lớp coloredpoint thừa kế từ 2 lớp trên như sau:
class coloredpoint:public point,public col
coloredpoint bị xoá.
Trang 424.1 Đặt vấn đề
TRONG HÀM THÀNH PHẦN CỦA LỚP DẪN XUẤT CÓ THỂ SỬ DỤNG TẤT
CẢ CÁC HÀM THÀNH PHẦN public (HOẶC protected ) CỦA LỚP CƠ SỞ.
KHI CÓ NHIỀU HÀM THÀNH PHẦN CÙNG TÊN TRONG CÁC LỚP KHÁC NHAU (ĐỊNH NGHĨA LẠI MỘT HÀM), TA CÓ THỂ LOẠI BỎ SỰ NHẬP
NHẰNG BẰNG CÁCH SỬ DỤNG TOÁN TỬ PHẠM VI “::”
point::display();
col::display();
Trang 434.1 Đặt vấn đề
Khai báo
coloredpoint p(3,9,2);
Câu lệnh p.display() gọi tới coloredpoint::display(),
Câu lệnh p.point::display() gọi tới point::display().
Trang 44class col {
unsigned color;
public:
col(unsigned c) {
cout<<"++Constr col \n"; color=c;
Trang 45}
~coloredpoint() { cout<<" Destr coloredpoint\n";
} void display() { point::display();
col::display();
} };
void main() {
clrscr();
coloredpoint p(3,9,2); cout<<" -\n"; p.display();
cout<<" -\n"; p.point::display();
cout<<" -\n"; p.col::display();
cout<<" -\n"; getch();
}
Trang 464.2 Danh sách móc nối đối tượng
Xây dựng một lớp, cho phép quản lý một danh sách móc nối các đối tượng kiểu point
head là con trỏ đối tượng, chỉ đến phần tử đầu tiên của danh sách móc nối.
Head
Head
Trang 474.2 Danh sách móc nối đối tượng
Mỗi phần tử của danh sách chứa:
Một con trỏ đến phần tử tiếp theo,
Một con trỏ đến đối tượng chứa thông tin liên quan.
Lớp list để quản lý danh sách sẽ có ít nhất:
Một thành phần dữ liệu: con trỏ đến phần tử đầu tiên (head),
Một hàm thành phần có chức năng chèn vào danh sách một đối tượng tại
một địa chỉ nào đó chú ý rằng địa chỉ này phải có kiểu void * để đảm bảo sự
tương thích với các kiểu dữ liệu khác nhau.
Trang 484.2 Danh sách móc nối đối tượng
struct element //cấu trúc một phần tử của danh sách
list
{
element *next; //con trỏ đến thành phần đi sau
void *content; //con trỏ đến một đối tượng tuỳ ý
Trang 494.2 Danh sách móc nối đối tượng
Để quản lý danh sách ta có thể thêm các chức năng xử lý khác như:
Hiển thị các đối tượng được chỉ bởi danh sách
Tìm kiếm một phần tử
Xoá một phần tử
Các thao tác trên danh sách được nêu ra ở trên đều gắn với cơ chế duyệt danh sách Việc duyệt này cần phải được kiểm soát từ bên ngoài danh sách nhưng có thể dựa trên các thành phần hàm cơ sở là:
Khởi tạo việc duyệt,
Chuyển sang phần tử tiếp theo.