- 多继承的名字冲突问题 {#title-0} ========================
多继承:一个类可以同时继承多个类,但是多继承很容易产生同名冲突(函数、变量)。
class A
{
public:
int m_a;
int m_b;
};
class B
{
public:
int m_a;
int m_b;
};
class CCCC : public A, public B {};
void test01()
{
CCCC c;
c.A::m_a;
c.B::m_a;
}
对于上述情况,如果由于多继承出现的名字冲突问题,需要通过指定类作用域的方式加以区分。
- 多继承的优点 {#title-1} ====================
多继承并不是一无是处,也会有自己的一些使用场景,通过多继承可以复用多个类的代码和数据。在不影响类继承的情况下,赋予子类一些功能。比如:想要实现,统计 Cat 和 Dog 的对象创建个数:
class ObjectCount { public: ObjectCount() { ++m_cnt; cout << "当前 Animal 对象个数:" << m_cnt << endl; }
~ObjectCount() { --m_cnt; cout << "当前 Animal 对象个数:" << m_cnt << endl; }
public: static int m_cnt; };
int ObjectCount::m_cnt = 0;
class Animal { public: void walk() { cout << "走路" << endl; } public: int m_type; // 动物的品种 };
// Cat 类只需要多继承一个 ObjectCount 类,就可以自动实现对象创建的个数统计 // Cat 类创建对象时,会调用父类的构造函数 ObjectCount ,从而对对象个数进行累加 // Cat 类对象销毁时,会调用父类的析构函数 ObjectCount ,从而减少对象的个数 class Cat : public Animal, public ObjectCount {}; class Dog : public Animal, public ObjectCount {};
void test02() { Cat c1, c2, c3; Dog d1, d2; }
- 菱形继承问题 {#title-2} ====================
在 C++ 中的多继承过程中,还可能出现菱形继承问题(钻石继承),如下代码所示:
class BBB { public: int m_bbb; };
// AAA 类有两个成员变量: m_bbb、m_aaa class AAA : public BBB { public: int m_aaa; };
// CCC 类有两个成员变量: m_bbb、m_ccc class CCC : public BBB { public: int m_ccc; };
// DDD 类有 2 个 m_bbb, m_aaa、m_ccc、m_ddd class DDD : public AAA, public CCC { public: int m_ddd; };
// 问题:1. 二义性(对象中出现了两个同名成员) 2. 菱形继承顶层基类的数据成员的重复、冗余。 void test03() { DDD d; d.AAA::m_bbb; d.CCC::m_bbb; }
- 虚继承机制 {#title-3} ===================
虚继承解决的是菱形继承中,最顶层基类的数据冗余、二义性问题。
把最顶层的基类当做虚基类,告诉编译期它的数据是共享的,而不是随着继承层增多,数据冗余增多。
虚继承必须在运行阶段完成,编译阶段无法完成。
不建议大家使用多继承(菱形继承),并不是不能使用。
class BBB { public: int m_bbb; };
// AAA 类虚继承 BBB 类,BBB 类就叫做虚基类 class AAA : virtual public BBB { public: int m_aaa; };
// CCC 类虚继承 BBB 类 class CCC : virtual public BBB { public: int m_ccc; };
// DDD 类有 2 个 m_bbb, m_aaa、m_ccc、m_ddd class DDD : public AAA, public CCC { public: int m_ddd; };
void test03() { DDD d; d.m_bbb; d.AAA::m_bbb; d.CCC::m_bbb; }
虚基类的初始化问题
普通继承结构中,本类负责调用直接父类的构造函数初始化
虚继承结构中,每一个虚基类的子类,都需要负责虚基类的初始化,这样才能保证虚基类能够被初始化、并且只有一次被初始化。
默认情况下会调用虚基类的默认构造,当其不存在默认构造函数时,每一个虚基类的子类都需要使用初始化列表初始化虚基类
class BBB { public: BBB(int) { cout << "BBB 构造函数" << endl; }
~BBB() { cout << "BBB 析构函数" << endl; }
public: int m_bbb; };
// AAA 类虚继承 BBB 类,BBB 类就叫做虚基类 class AAA : virtual public BBB { public: AAA() : BBB(0) { cout << "AAA 构造函数" << endl; }
~AAA() { cout << "AAA 析构函数" << endl; }
public: int m_aaa; };
// CCC 类虚继承 BBB 类 class CCC : virtual public BBB { public: CCC() : BBB(0) { cout << "CCC 构造函数" << endl; }
~CCC() { cout << "CCC 析构函数" << endl; }
public: int m_ccc; };
// DDD 类创建对象的时候,直接调用了 BBB 类的构造函数 class DDD : public AAA, public CCC { public: DDD() : BBB(0) { cout << "DDD 构造函数" << endl; }
~DDD() { cout << "DDD 析构函数" << endl; }
public: int m_ddd; };
void test04() { DDD d; }