菱形继承的问题:从下面的对象成员模型构造,可以看出菱形继承有数据冗余和二义性的问题。在Assistant的对象中Person成员会有两份。
class Person
{
public:
string _name; // 姓名
};
class Student : public Person
{
protected:
int _num; //学号
};
class Teacher : public Person
{
protected:
int _id; // 职工编号
};
class Assistant : public Student, public Teacher
{
protected:
string _majorCourse; // 主修课程
};
void Test()
{
// 这样会有二义性无法明确知道访问的是哪一个
Assistant a;
a._name = "peter";//会报错("Assistant::_name不明确")
// 需要显示指定访问哪个父类的成员可以解决二义性问题,但是数据冗余问题无法解决
a.Student::_name = "xxx";
a.Teacher::_name = "yyy";
}
虚拟继承可以解决菱形继承的二义性和数据冗余的问题。如上面的继承关系,在Student和Teacher的继承Person时使用虚拟继承,即可解决问题。需要注意的是,虚拟继承不要在其他地方去使用。
class Person
{
public:
string _name; // 姓名
};
class Student : virtual public Person
{
protected:
int _num; //学号
};
class Teacher : virtual public Person
{
protected:
int _id; // 职工编号
};
class Assistant : public Student, public Teacher
{
protected:
string _majorCourse; // 主修课程
};
void Test()
{
Assistant a;
a._name = "peter";
}
为了研究虚拟继承原理,我们给出了一个简化的菱形继承继承体系,再借助内存窗口观察对象成员的模型。
#include <iostream>
using namespace std;
class A
{
public:
int _a;
};
// class B : public A
class B : virtual public A
{
public:
int _b;
};
// class C : public A
class C : virtual public A
{
public:
int _c;
};
class D : public B, public C
{
public:
int _d;
};
int main()
{
D d;
d.B::_a = 1;
d.C::_a = 2;
d._b = 3;
d._c = 4;
d._d = 5;
return 0;
}
不使用虚继承(class C : public A):
使用虚继承(class C : virtual public A)
继承(Inheritance)和组合(Composition)是面向对象编程中用于构建类之间关系的两种主要方式。
继承是一种"is-a"关系,其中一个类可以继承另一个类的属性和行为。通过继承,子类可以共享父类的成员变量和成员函数,并且可以添加自己的特定功能。继承适用于具有层次结构的类,其中子类是父类的特殊化。
组合是一种"has-a"关系,其中一个类包含另一个类的对象作为成员变量。通过组合,一个类可以使用其他类的对象来实现自己的功能,而不需要继承其所有特性。组合适用于在一个类中使用其他类的功能,而不需要与其形成层次结构。
当需要表示一种类与类之间的层次结构、子类是父类的特殊化关系时,可以使用继承。继承可以提供代码重用和多态性的好处。当需要在一个类中使用另一个类的功能、将一个类作为另一个类的组成部分时,可以使用组合。组合可以实现代码模块化和灵活性。
在选择继承或组合时,需要考虑类之间的关系和需求。如果存在"is-a"关系,且子类可以完全继承父类的属性和行为,可以选择继承。如果存在"has-a"关系,且一个类需要使用另一个类的功能,可以选择组合。此外,还应考虑代码的可维护性、扩展性和设计的灵活性。
(本章完)