C++ 多继承中的虚继承机制

1. 多继承的名字冲突问题

多继承:一个类可以同时继承多个类,但是多继承很容易产生同名冲突(函数、变量)。

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;
}

对于上述情况,如果由于多继承出现的名字冲突问题,需要通过指定类作用域的方式加以区分。

2. 多继承的优点

多继承并不是一无是处,也会有自己的一些使用场景,通过多继承可以复用多个类的代码和数据。在不影响类继承的情况下,赋予子类一些功能。比如:想要实现,统计 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;
}

3. 菱形继承问题

在 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;
}

4. 虚继承机制

虚继承解决的是菱形继承中,最顶层基类的数据冗余、二义性问题。
把最顶层的基类当做虚基类,告诉编译期它的数据是共享的,而不是随着继承层增多,数据冗余增多。
虚继承必须在运行阶段完成,编译阶段无法完成。
不建议大家使用多继承(菱形继承),并不是不能使用。

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;
}
未经允许不得转载:一亩三分地 » C++ 多继承中的虚继承机制
评论 (0)

9 + 5 =