Nội dung
Để biết được vấn đề kim cương trong đa thừa kế. Trước tiên, ta cần hiểu đa thừa kế là gì?
Đa thừa kế trong C ++
Đa thừa kế là một tính năng của C++ trong đó một lớp có thể kế thừa từ nhiều hơn một lớp. Các hàm tạo của các lớp kế thừa được gọi theo thứ tự mà chúng được kế thừa. Ví dụ, trong chương trình sau, phương thức khởi tạo của B được gọi trước phương thức khởi tạo của A.
#include <iostream>
using namespace std;
class A
{
public:
A() { cout << "A's constructor called" << endl; }
};
class B
{
public:
B() { cout << "B's constructor called" << endl; }
};
class C : public B, public A // Note the order
{
public:
C() { cout << "C's constructor called" << endl; }
};
int main()
{
C c;
return 0;
}
Kết quả
B's constructor called
A's constructor called
C's constructor called
Các hàm hủy được gọi theo thứ tự ngược lại của các hàm tạo.
Vấn đề kim cương (diamond problem)
Vấn đề kim cương xảy ra khi hai lớp cha của một lớp có một lớp cơ sở chung. Trong sơ đồ trên, lớp TA nhận được hai bản sao của tất cả các thuộc tính của lớp Person, điều này gây ra sự mơ hồ. Ví dụ, hãy xem xét chương trình sau đây.
#include <iostream>
using namespace std;
class Person
{
// Data members of person
public:
Person(int x) { cout << "Person::Person(int ) called" << endl; }
};
class Faculty : public Person
{
// data members of Faculty
public:
Faculty(int x) : Person(x)
{
cout << "Faculty::Faculty(int ) called" << endl;
}
};
class Student : public Person
{
// data members of Student
public:
Student(int x) : Person(x)
{
cout << "Student::Student(int ) called" << endl;
}
};
class TA : public Faculty, public Student
{
public:
TA(int x) : Student(x), Faculty(x)
{
cout << "TA::TA(int ) called" << endl;
}
};
int main()
{
TA ta1(30);
}
Person::Person(int ) called
Faculty::Faculty(int ) called
Person::Person(int ) called
Student::Student(int ) called
TA::TA(int ) called
Trong chương trình trên, hàm tạo của ‘Person’ được gọi hai lần. Hàm hủy của ‘Person’ cũng sẽ được gọi hai lần khi đối tượng ‘ta1’ bị hủy. Vì vậy, đối tượng ‘ta1’ có hai bản sao của tất cả các thành viên của ‘Person’, điều này gây ra sự mơ hồ. Giải pháp cho vấn đề này là từ khóa ‘virtual’. Chúng tôi làm cho các lớp ‘Faculty’ và ‘Student’ làm lớp cơ sở ảo để tránh hai bản sao của ‘Person’ trong lớp ‘TA’. Ví dụ, hãy xem xét chương trình sau đây.
#include <iostream>
using namespace std;
class Person
{
public:
Person(int x) { cout << "Person::Person(int ) called" << endl; }
Person() { cout << "Person::Person() called" << endl; }
};
class Faculty : virtual public Person
{
public:
Faculty(int x) : Person(x)
{
cout << "Faculty::Faculty(int ) called" << endl;
}
};
class Student : virtual public Person
{
public:
Student(int x) : Person(x)
{
cout << "Student::Student(int ) called" << endl;
}
};
class TA : public Faculty, public Student
{
public:
TA(int x) : Student(x), Faculty(x)
{
cout << "TA::TA(int ) called" << endl;
}
};
int main()
{
TA ta1(30);
}
Person::Person() called
Faculty::Faculty(int ) called
Student::Student(int ) called
TA::TA(int ) called
Trong chương trình trên, hàm tạo của ‘Person’ được gọi một lần. Một điều quan trọng cần lưu ý trong kết quả ở trên là, hàm tạo mặc định của ‘Person’ được gọi. Khi chúng ta sử dụng từ khóa ‘virtual’, hàm tạo mặc định của lớp ông nội được gọi theo mặc định ngay cả khi các lớp cha gọi hàm tạo được tham số hóa một cách rõ ràng.
Làm cách nào để gọi hàm tạo tham số của lớp ‘Person’?
Hàm tạo phải được gọi trong lớp ‘TA’. Ví dụ, hãy xem chương trình sau.
#include <iostream>
using namespace std;
class Person
{
public:
Person(int x) { cout << "Person::Person(int ) called" << endl; }
Person() { cout << "Person::Person() called" << endl; }
};
class Faculty : virtual public Person
{
public:
Faculty(int x) : Person(x)
{
cout << "Faculty::Faculty(int ) called" << endl;
}
};
class Student : virtual public Person
{
public:
Student(int x) : Person(x)
{
cout << "Student::Student(int ) called" << endl;
}
};
class TA : public Faculty, public Student
{
public:
TA(int x) : Student(x), Faculty(x), Person(x)
{
cout << "TA::TA(int ) called" << endl;
}
};
int main()
{
TA ta1(30);
}
Person::Person(int ) called
Faculty::Faculty(int ) called
Student::Student(int ) called
TA::TA(int ) called
Nói chung, không được phép gọi hàm tạo của ông nội trực tiếp, nó phải được gọi thông qua lớp cha. Nó chỉ được phép khi từ khóa “virtual” được sử dụng.
Như một bài tập, hãy dự đoán kết quả của các chương trình sau.
Câu hỏi 1
#include <iostream>
using namespace std;
class A
{
int x;
public:
void setX(int i) { x = i; }
void print() { cout << x; }
};
class B : public A
{
public:
B() { setX(10); }
};
class C : public A
{
public:
C() { setX(20); }
};
class D : public B, public C
{
};
int main()
{
D d;
d.print();
return 0;
}
Câu hỏi 2
#include <iostream>
using namespace std;
class A
{
int x;
public:
A(int i) { x = i; }
void print() { cout << x; }
};
class B : virtual public A
{
public:
B() : A(10) {}
};
class C : virtual public A
{
public:
C() : A(10) {}
};
class D : public B, public C
{
};
int main()
{
D d;
d.print();
return 0;
}
Vui lòng viết bình luận nếu bạn thấy bất kỳ điều gì không chính xác, hoặc bạn muốn chia sẻ thêm thông tin về chủ đề đã thảo luận ở trên.
Để lại một bình luận