1、多重继承的问题:在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,就会出现下面这种情况
这个时候仍然会存在二义性,二义性是由于他们有公共的基类造成的,比如下面这个例子:
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的虚表应该是下面这个样子的
会不会有问题:不会有问题的,完全正常
- 多重继承,只有类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的内存结构会是下面这样
可以看到它将自己的虚函数,放在了第一个虚表中,原因是和它的继承顺序有关系