- 多继承的名字冲突问题 {#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;
}