1、多重继承的问题:在C++类的继承中会遇到这样一个问题,一个派生类有两个或者两个以上的基类,如同下面这种继承情况

C++ 菱形继承_虚函数表

类C继承了类A和类B,但是类A和类B里面有着相同的成员变量,那么类C在使用这个成员变量的时候就会出现二义性的问题,需要通过域成员运算符进行区分

class A {
public:
	A(){}
	~A(){}

	void Display() {
		std::cout << "A::Hello" << std::endl;
	}

protected:
	uint32_t data_ = 10;
};

class B {
public:
	B() {}
	~B() {}

	void Display() {
		std::cout << "B::Hello" << std::endl;
	}

protected:
	uint32_t data_ = 11;
};

class C :public A, public B {
public:
	C(){}
	~C(){}

	void Show() {
		std::cout << data_ << std::endl; // 错误,无法识别data_是类A的还是类B的
		std::cout << A::data_ << std::endl; // 正确
		Display(); // 错误,二义性
		A::Display(); // 正确
	}
};

2、菱形继承的问题,假设上面的类A和类B又继承一个公共的类Base,就会出现下面这种情况

 C++ 菱形继承_二义性_02

 

 这个时候仍然会存在二义性,二义性是由于他们有公共的基类造成的,比如下面这个例子:

class Base {
public:
	Base(){}
	~Base(){}

	void Display() {
		std::cout << "Base::Hello" << std::endl;
	}

protected:
	uint32_t data_ = 11;
};

class A: public Base {
public:
	A(){}
	~A(){}

protected:
	uint32_t data_a_ = 10;
};

class B : public Base {
public:
	B() {}
	~B() {}

protected:
	uint32_t data_b_ = 11;
};

class C :public A, public B {
public:
	C(){}
	~C(){}

	void Show() {
		std::cout << data_ << std::endl; // 编译错误,二义性问题
		std::cout << A::data_ << std::endl;
		Display();  //编译错误,二义性问题
		A::Display();
	}
};

出现二义性的原因就是类A和类B都继承了类Base的成员和函数 ,而且会在类C中存在两份基类Base,会浪费空间

3、为了解决上诉问题,C++引入了虚基类,虚继承是一种机制,类通过虚继承指出它希望共享虚基类的状态。对给定的虚基类,无论该类在派生层次中作为虚基类出现多少次,只继承一个共享的基类子对象,共享基类子对象称为虚基类

class Base {
public:
	Base() {}
	~Base() {}

	void Display() {
		std::cout << "Base::Hello" << std::endl;
	}

protected:
	uint32_t data_ = 11;
};

class A : virtual public Base {
public:
	A() {}
	~A() {}

protected:
	uint32_t data_a_ = 10;
};

class B : virtual public Base {
public:
	B() {}
	~B() {}

protected:
	uint32_t data_b_ = 12;
};

class C :public A, public B {
public:
	C() {}
	~C() {}

	void Show() {
		std::cout << data_ << std::endl; // 正常,输出11
		std::cout << A::data_ << std::endl;
		Display();  // 正常,输出"Base::Hello"
		A::Display();
	}
};

可以看到,虚基类可以解决菱形继承二义性的问题,可以直接让C类使用Base类的成员变量和函数

4、面试过程中被遇到的关于菱形继承的问题

  • 类Base有多个虚函数,类A和类B也重写了Base中的这个虚函数,类C的虚函数表应该是怎么样的,使用指向C的指针,调用这个虚函数会不会有问题
class Base {
public:
	Base() {}
	~Base() {}

	virtual void Print() {
		std::cout << "Base::Print" << std::endl;
	}
};

class A : public Base {
public:
	A() {}
	~A() {}

	virtual void Print() {
		std::cout << "A::Print" << std::endl;
	}

	virtual void Show() {
		std::cout << "A::Show" << std::endl;
	}
};

class B : public Base {
public:
	B() {}
	~B() {}

	virtual void Print() {
		std::cout << "B::Print" << std::endl;
	}

	virtual void Show() {
		std::cout << "B::Show" << std::endl;
	}
};

class C :public A, public B {
public:
	C() {}
	~C() {}

	virtual void Show() {
		std::cout << "C::Show" << std::endl;
	}

	virtual void Display() {
		std::cout << "C::Display" << std::endl;
	}
};

    虚函数表:其实这个时候使用sizeof(C)计算C的大小,会发现C的大小是8,因为这个时候的C有两个虚函数表,分别是A的和B的,并且C重写了对应的虚函数,那么这个时候C的虚表应该是下面这个样子的

C++ 菱形继承_虚函数_03

    会不会有问题:不会有问题的,完全正常

  • 多重继承,只有类A,B,C,类A中有一个虚函数,类B中也有一个同名的虚函数,类C中没有重写这个虚函数,这个使用使用类C的对象去调用这个虚函数,会有什么问题
class A {
public:
	A(){}
	~A(){}

	virtual void Show() {
		std::cout << "A::Show" << std::endl;
	}
};

class B {
public:
	B() {}
	~B() {}

	virtual void Show() {
		std::cout << "B::Show" << std::endl;
	}
};

class C :public A, public B {
public:
	C() {}
	~C() {}
};

int main(void) {
	C c;
	c.Show();
	return 0;
}

答案:仍然是会报二义性的错误

解决办法:

加上作用域去调用

C c;
c.A::Show();
c.B::Show();

使用多态的思想,基类指针,子类对象

A* test = new C();
test->Show();
  • 多重继承问题:类A和类B有多个虚函数,类C继承A和B但是只是重写了一个虚函数,并且类C中有自己的虚函数,请问类C的虚函数表应该是怎样的,比如下面这个例子 
class A {
public:
	A() {}
	~A() {}

	virtual void Print() {
		std::cout << "A::Print" << std::endl;
	}

	virtual void Show() {
		std::cout << "A::Show" << std::endl;
	}
};

class B {
public:
	B() {}
	~B() {}

	virtual void Print() {
		std::cout << "B::Print" << std::endl;
	}

	virtual void Show() {
		std::cout << "B::Show" << std::endl;
	}
};

class C :public A, public B {
public:
	C() {}
	~C() {}

	virtual void Show() {
		std::cout << "C::Show" << std::endl;
	}

	virtual void Display() {
		std::cout << "C::Display" << std::endl;
	}
};

其实如果我们计算sizeof(C)的大小会发现是8,为什么呢?因为它有两个指针,分别指向两个虚表, 类C的内存结构会是下面这样

C++ 菱形继承_虚函数_03

可以看到它将自己的虚函数,放在了第一个虚表中,原因是和它的继承顺序有关系