C++多重继承与虚基类
C++多重继承与虚基类
1.多重继承
多重继承就是一个派生类继承了多个基类。
I>初始化
i>通过成员初始化列表指定构造函数
class D : public B, public C {
private:
int a;
public:
D(int a1, int a2, int a3) : B(a1),C(a2),a(a3) {
cout << "D::D()" << endl;
}
};
ii>构造函数调用顺序
class B {
private:
int a;
public:
B(int a) : a(a){
cout << "B::B()" << endl;
}
void f() {
cout << "B::f() ------ a = " << a << endl;
}
};
class C {
private:
int a;
public:
C(int a) : a(a) {
cout << "C::C()" << endl;
}
void f() {
cout << "C::f() ------ a = " << a << endl;
}
};
class D : public B, public C {
private:
int a;
public:
D(int a1, int a2, int a3) : a(a3),B(a1),C(a2) {
cout << "D::D()" << endl;
}
void f() {
B::f();
C::f();
cout << "D::f() ------ a = " << a << endl;
}
};
int main() {
D obj(1, 2, 3);
obj.f();
return 0;
}
输出:
B::B()
C::C()
D::D()
B::f() ------ a = 1
C::f() ------ a = 2
D::f() ------ a = 3
调用构造函数的顺序与继承顺序一致
II>解决冲突
i>方法冲突
在派生类中重定义方法(用上例的方法)
void D::f(){
B::f();
C::f();
...
}
或者在外部调用的时候加上域解析运算符
objD.B::f();//派生类对象调用基类B的方法f
objD.C::f();//派生类对象调用基类C的方法f
ii>成员冲突(经典的菱形继承)
class A {
private:
int a;
...
};
class B : public A{
...
};
class C : public A{
...
};
class D : public B, public C {
...
};
可以看到,对于类A的成员,继承了两次,也就是说,D中,有两个重复成员a的来自类A,这是不必要的,所以,有了新的继承方法,虚继承
2.虚基类
I>虚继承
在继承时,加上virtual关键字,表示虚继承
class A {
};
class B : virtual public A {
};
int main() {
cout << sizeof(A) << endl;
cout << sizeof(B) << endl;
return 0;
}
输出:
1
4
可以发现,类B中有了隐藏的成员(如果是普通的继承,只占1个字节),其实,这个隐藏的成员存储了类B对象到类A对象的一个间接引用(存了一个整数,是一个偏移量)
虚继承中的基类被称为虚基类
II>通过虚继承解决成员冲突问题
class A {
private:
int a;
...
};
class B : virtual public A{
...
};
class C : virtual public A{
...
};
class D : public B, public C {
...
};
通过虚继承,当D创建实例时,只会继承“一份A”(B,C中存了间接引用)。
III>虚基类的初始化——间接派生类的初始化成员列表
D::D(int a) : A(a) {...}
注意,A是D的间接基类,如果A是非虚基类,这里是非法的,一般情况下,只能指定自己的直接基类怎样调用构造;如果是虚基类,想要调用有参的构造,就要在间接基类中的成员初始化列表中指定虚基类调用哪个构造。不能通过B和C的构造函数进行传递,因为只有一份A,两条初始化路径可能会造成二义性,所以,采用这样的方式初始化虚基类。
IV>虚基类还改变了对于方法二义性的解释——派生类的名称优先于直接或间接基类的名称
先来看普通的继承:
class A {
public:
void f() {
cout << "A::f()" << endl;
}
};
class B : public A {
public:
void f() {
cout << "B::f()" << endl;
}
};
class C : public A {
};
class D : public B, public C{
};
int main() {
D objd;
objd.f();
return 0;
}
这里会出现二义性调用,D中有B::f()和A::f()
对于虚继承:
class A {
public:
void f() {
cout << "A::f()" << endl;
}
};
class B : virtual public A {
public:
void f() {
cout << "B::f()" << endl;
}
};
class C : virtual public A {
};
class D : public B, public C{
};
int main() {
D objd;
objd.f();
return 0;
}
输出:
B::f()
同样,D中有B::f()和A::f(),但B是A的子类,则,B::f更优先。 个人觉得在使用时还是加上域解析运算符,这样不容易出错