Giới thiệu [2/10] Mỗi nhóm con là một lớp các đối tượng tương tự, nhưng giữa các nhóm con có chung một số đặc điểm Quan hệ giữa các nhóm con với nhóm lớn được gọi là quan hệ “là một” is-
Trang 3Giới thiệu [1/10]
Ngoài việc nhóm các đối tượng có cùng tập thuộc tính/hành vi lại với nhau, con người thường nhóm các đối tượng có cùng một số thuộc tính/ hành vi
Ví dụ: nhóm tất cả xe chạy bằng động cơ thành một nhóm, rồi phân thành các nhóm nhỏ hơn tuỳ theo loại xe (xe ô tô, xe tải, )
Trang 4Giới thiệu [2/10]
Mỗi nhóm con là một lớp các đối tượng tương tự, nhưng giữa các nhóm con có chung một số đặc điểm
Quan hệ giữa các nhóm con với nhóm lớn được gọi
là quan hệ “là một” (is-a)
Trang 5Giới thiệu [3/10]
Ví dụ:
o Một cái xe ô tô “là một” xe động cơ
o Một cái xe tải “là một” xe động cơ
o Một cái xe máy “là một” xe động cơ
Dùng cấu trúc hướng đối tượng để định nghĩa quan
hệ “là một”
Trang 7Giới thiệu [5/10]
Mối liên kết giữa các lớp trong quan hệ “là một” xuất phát từ thực tế rằng các lớp con cũng có mọi thuộc tính/ hành vi của lớp cha, và cộng thêm các thuộc tính/ hành vi khác
Trang 8Giới thiệu [6/10]
Lớp cha – superclass (hoặc lớp cơ sở - base class)
Lớp tổng quát hơn trong mối quan hệ “là một”
Các đối tượng thuộc lớp cha có cùng tập thuộc tính
và hành vi
Trang 9Giới thiệu [7/10]
Lớp con – subclass (hoặc lớp dẫn xuất – derived class)
Lớp cụ thể hơn trong một quan hệ “là một”
Các đối tượng thuộc lớp con có cùng tập thuộc tính và hành vi (do kế thừa từ lớp cha), kèm thêm
tập thuộc tính và hành vi của riêng lớp con
Trang 11Giới thiệu [9/10]
Ưu điểm của việc kế thừa
Tiết kiệm thời gian và công sức
Tái sử dụng lại những lớp có sẵn
Giảm lượng code phải thiết kế, viết, kiểm tra
Tránh trùng lắp code
Rút ngắn thời gian giúp LTV tập trung vào mục tiêu
Giúp phân loại và thiết kế lớp dễ dàng, dễ quản lý
Trang 13Sơ đồ quan hệ đối tượng [1/3]
(Object Relationship Diagram – ORD)
Trang 14Sơ đồ quan hệ đối tượng [2/3]
Biểu diễn các thành phần:
private: thêm dấu trừ phía trước tên
public: thêm dấu cộng phía trước tên
Trang 15Sơ đồ quan hệ đối tượng [3/3]
Đối với những lớp dẫn xuất, chỉ cần liệt kê các thuộc tính/ hành vi mà lớp cơ sở không có
Đơn giản hoá sơ đồ
Nhấn mạnh các điểm
khác biệt
Trang 16Cây kế thừa [1/2]
Các quan hệ kế thừa luôn được biểu diễn với các lớp dẫn xuất đặt dưới lớp cơ sở để nhấn mạnh bản chất phả hệ của quan hệ
Trang 18Kế thừa vs Quan hệ khác
• Đây là quan hệ không dựa trên kế
thừa
• Mối quan hệ này gọi là quan hệ
“có một” (has-a) cũng được gọi là
quan hệ bao gộp (aggregation)
Trang 20public public trong lớp
Dấu trong lớp dẫn xuất
public protected private
Trang 21VD - đơn kế thừa [1/4]
class CMyPoint {
protected:
float x,y;
public:
CMyPoint(float a= 0, float b= 0); void SetPoint(float a, float b); float GetX() const
{
return x;
} float GetY() const {
return y;
} void Print() const;
};
Trang 22VD - đơn kế thừa [2/4]
CMyPoint::CMyPoint(float a, float b) {
SetPoint(a, b);
} void CMyPoint::SetPoint(float a, float b) {
x = a;
y = b;
} void CMyPoint::Print() const {
cout << "(" << x << ", " << y << ")" << endl; }
Trang 23void SetCircle(float r, float a, float b);
float GetRadius() const;
float Area() const;
void Print() const;
radius = r;
}
Trang 24return radius;
} float CCircle::Area() const {
return 3.14159f * radius * radius;
} void CCircle::Print() const {
cout<<"[ ";
CMyPoint::Print();
cout<<", " << radius <<"]"<<endl;
}
Trang 25Constructor & destructor trong kế thừa [1/5]
Muốn chỉ định constructor cụ thể của lớp cơ sở
Constructor của lớp dẫn xuất phải chứa các thông tin làm tham số cho constructor của lớp cơ sở
Gọi constructor của lớp cở sở bằng cách sử dụng
bộ khởi tạo trong constructor của lớp dẫn xuất (cú pháp giống bộ khởi tạo thành viên)
Trang 26Constructor & destructor trong kế thừa [2/5]
Thứ tự gọi constructor và destructor
CCircle::CCircle(float r, float a, float b) : CMyPoint(a, b)
//a, b là các tham số của constructor cơ sở
Trang 27Constructor & destructor trong kế thừa [3/5]
Khi khai báo một đối tượng của lớp dẫn xuất thì constructor của lớp cơ sở được gọi trước đến constructor của lớp dẫn xuất
Destructor thực hiện ngược lại
Trang 28cout <<
"A"<<endl;
}
~A() {
cout<<"~A"<<endl;
} };
class B: public A {
public:
B() {
cout <<
"B"<<endl;
}
~B() {
cout<<"~B"<<endl;
} };
Trang 29cout <<
"C"<<endl;
}
~C() {
cout<<"~C"<<endl;
} };
void main() {
B b;
C c;
}
?
Trang 30Muốn truy cập phiên bản được định nghĩa trong lớp
cơ sở từ lớp dẫn xuất phải sử dụng toán tử phạm vi
Trang 32cout<<endl;
} };
class B: public A {
public:
void Print() {
cout<<"B::Print()";
cout<<endl;
} void Fun1() {
Print();
//B::Print();
} void Fun2() {
A::Print();
} };
B b;
b.Fun1();
b.Fun2();
b.Print(); //B::Print(); b.A::Print();
Trang 33Sự chuyển kiểu [1/7]
Một đối tượng của lớp dẫn xuất có thể sử dụng với
tư cách là một đối tượng của lớp cơ sở (chuyển kiểu ngầm định) nhưng ngược lại thì không
CMyPoint p;
CCircle c(10,20, 100);
p = c; //OK p.Print(); //CMyPoint::Print();
CMyPoint p2(50, 30);
c = p2; // Error
Slicing
Trang 35Upcast
Trang 36Sự chuyển kiểu [4/7]
Tương tự đối với biến tham chiếu
Upcast là quá trình tương tác với thực thể của lớp dẫn xuất như thể nó là thực thể của lớp cơ sở
void Fun(A & a) {
… }
Trang 37Sự chuyển kiểu [5/7]
Upcast thường sử dụng trong các tham số của hàmTham số hình thức là một con trỏ/tham chiếu đến lớp cơ sở được yêu cầu
nhưng tham số thực là con trỏ/tham chiếu đến lớp dẫn xuất cũng được chấp nhận
Trang 38Sự chuyển kiểu [6/7]
Downcast là quy trình ngược lại, đổi kiểu con trỏ/tham chiếu tới lớp cơ sở thành con trỏ/tham chiếu tới lớp dẫn xuất
Downcast là quy trình rắc rối hơn và có nhiều điểm không an toàn
Đây không phải là một quy trình tự động - nó luôn đòi hỏi đổi kiểu tường minh (explicit type cast)
Trang 39Sự chuyển kiểu [7/7]
Nếu ta biết chắc chắn một con trỏ lớp cơ sở đang trỏ tới một lớp dẫn xuất, ta có thể tự đổi kiểu cho con trỏ lớp cơ sở bằng cách sử dụng chuyển kiểu tường minh
Trang 40Đa kế thừa [1/6]
• Là khả năng một lớp có nhiều lớp cơ sở
• Cây kế thừa phức tạp lên rất nhiều
• Có thể sinh ra các vấn đề nhập nhằng (do các tên trùng nhau thành viên)
Bicycle
FishPerson
Fish
Trang 42}
float Area() {
return radius*radius*3.14f;
} };
class CTable {
protected:
float height;
{
return height;
} };
Trang 43return color;
} };
CRoundTable::CRoundTable(float h, float r, int c):CTable(h), CCircle(r) {
color=c;
}
Đa kế thừa [4/6]
CRoundTable table(1, 0.5f, 5);
cout << "Thong tin ve ban:" << endl;
cout << "Chieu cao:" << table.Height() << endl;
cout << "Dien tich:" << table.Area() << endl;
cout << "Mau: ” << table.Color << endl;
Trang 44Đa kế thừa – Constructor & destructor [5/6]
Các tham số của constructor của tất cả các lớp cơ
sở này được khai báo trong constructor của lớp dẫn xuất và constructor cơ sở cũng phải được khởi tạo
Constructor lớp cở sở xuất hiện trước sẽ thực hiện trước và cuối cùng mới tới constructor lớp dẫn xuấtĐối với destructor có trình tự thực hiện theo thứ tự ngược lại
Trang 45};
class B {
public:
void Method();
… };
class C: public A, public B {
… };
Trang 46class C: public A {
};
Giải pháp: Khai báo lớp A là lớp
cơ sở ảo cho cả 2 lớp B và C
Trang 47Lớp cơ sở ảo [2/4]
Không thể khai báo hai lần cùng một lớp trong danh sách của các lớp cơ sở cho một lớp dẫn xuất.Tuy nhiên vẫn có thể có trường hợp cùng một lớp
cơ sở được đề cập nhiều hơn một lần
Điều này phát sinh lỗi vì không có cách nào để phân biệt hai lớp cơ sở gốc
Trang 49?
Trang 50Nếu sử dụng đa kế thừa, nhất thiết phải cân nhắc về các xung đột có thể nảy sinh trong khi sử dụng các lớp có liên quan
Trang 51Q&A