MI(多重继承)
上图
很好理解,B,C有着一样的祖先,D的祖先又是B,C像棱形一样。
MI有时候很方便,但是带来的是很多难以发现的问题
1.派生类中多继承一份基类
2.同名函数的处理
3.类方法递增导致类方法重复
4.基类中的构造函数怎么办
下面是解决办法和一些基本规则
主要的设计难点:类内组件如何用保护权限构建。
其他的语法都很简单,编程学的不是语法,而是学的思想
#include <iostream>
#define ENDL cout << endl;
using namespace std;
class Base
{
private:
int base;
public:
explicit Base(int _base) : base(_base) {}
void show() const
{
cout << base;
}
protected:
void data() const
{
cout << base;
}
};
//语法
//用virtual关键字继承,这种继承不像是虚函数中的virtual,他可以说是关键字重载
//不用virtual 将会导致AB中出现两份Base对象
class A :virtual public Base
{
private:
int a;
public:
A(int _base, int _a) : Base(_base), a(_a) {}
void show()
{
Base::show();
cout << a;
}
public:
void data() const
{
cout << a;
}
};
class B : virtual public Base
{
private:
int b;
public:
B(int _base, int _b) : Base(_base), b(_b) {}
void show() const
{
Base::show();
cout << b;
}
public:
void data() const
{
cout << b;
}
};
//Base 是虚基类,虽然其中没有虚函数
class AB :public A,public B
{
private:
int ab;
public:
AB(int _base, int _a, int _b, int _ab) : Base(_base) , A(_base, _a), B(_base,_b), ab(_ab){}
void show()
{
A::show();
B::show();
cout << ab;
}
void showPlus()
{
Base::data();
AB::data();
cout << ab;
}
protected:
void data()
{
A::data();
B::data();
}
};
int main()
{
AB temp = AB(0, 1, 2, 3);
//不能直接访问show会出现二义性,即编译器不知道用哪个基类中的show方法
//应使用作用域解析符号,或者在派生类中重定义
//当然重定义会带来很多问题,我们也有解决的办法
temp.Base::show();
ENDL
temp.A::show();
ENDL
temp.B::show();
//派生类中重定义
//这将会导调用两次基类中show()方法
ENDL
temp.show(); //或者显式的调用 temp.AB::show();
ENDL
temp.showPlus();
return 0;
}
这时候protected权限的作用就来了,我么又想能够访问让派生类访问提供的data组件,又不想让类外访问到data组件,那么用保护权限就可以完美的解决这个问题
关于构造函数:
我们看一看构造函数的语法形式
explicit Base(int _base) : base(_base) {}
A(int _base, int _a) : Base(_base), a(_a) {}
B(int _base, int _b) : Base(_base), b(_b) {}
AB(int _base, int _a, int _b, int _ab) : Base(_base) , A(_base, _a), B(_base,_b), ab(_ab){}
使用的是列表初始化,其实我们可以看到在Base, A, B中的构造函数形式就是简单的列表初始化,但在AB中 我们要用到新的构造函数规则
对于虚基类,构造函数的自动传递(即有派生类调用基类的构造函数)将不起作用;在自动传递信息时,将通过两条不同的途径自动传递给基类对象。为了避免这种冲突,C++在基类是虚的时,禁止通过中间类自动传递给基类。因此必须在初始化派生类之前,显式的调用基类中的构造函数,尽管中间类中的语法看似让基类初始化,但实际上并不起作用。如果没有显式的调用,编译器将自动调用默认构造函数。
总结:多重继承问题多,先是基类成员和方法继承了多份,这个只要用virtual。主要避免二义性,加上作用域解析符。类内组件提供用portected,向下继承避免重复调用。