多重继承容易出现的问题:命名冲突和数据冗余问题。
#include <iostream>
using namespace std;
// 间接基类
class Base1
{
public:
int var1;
};
// 直接基类
class Base2 : public Base1
{
public:
int var2;
};
// 直接基类
class Base3 : public Base1
{
public:
int var3;
};
// 派生类
class Derive : public Base2, public Base3
{
public:
void set_var1(int tmp) { var1 = tmp; } // error: reference to 'var1' is ambiguous. 命名冲突
void set_var2(int tmp) { var2 = tmp; }
void set_var3(int tmp) { var3 = tmp; }
void set_var4(int tmp) { var4 = tmp; }
private:
int var4;
};
int main()
{
Derive d;
return 0;
}
上述代码中存的问题:
对于派生类 Derive 上述代码中存在直接继承关系和间接继承关系。
直接继承:Base2 、Base3
间接继承:Base1
对于派生类中继承的的成员变量 var1 ,从继承关系来看,实际上保存了两份,一份是来自基类 Base2,一份来自基类 Base3。因此,出现了命名冲突。
解决方法1: 声明出现冲突的成员变量来源于哪个类(作用域分辨符)
#include <iostream>
using namespace std;
// 间接基类
class Base1
{
public:
int var1;
};
// 直接基类
class Base2 : public Base1
{
public:
int var2;
};
// 直接基类
class Base3 : public Base1
{
public:
int var3;
};
// 派生类
class Derive : public Base2, public Base3
{
public:
void set_var1(int tmp) { Base2::var1 = tmp; } // 这里声明成员变量来源于类 Base2,当然也可以声明来源于类 Base3
void set_var2(int tmp) { var2 = tmp; }
void set_var3(int tmp) { var3 = tmp; }
void set_var4(int tmp) { var4 = tmp; }
private:
int var4;
};
int main()
{
Derive d;
return 0;
}
解决方法二: 将共同基类设置为虚基类
保证从不同路径过来的同名数据成员在内存中就只有一个副本,同一个函数名也只有一个映射。
实现方式:在继承方式前面加上 virtual 关键字。
#include <iostream>
using namespace std;
// 间接基类,即虚基类
class Base1
{
public:
int var1;
};
// 直接基类
class Base2 : virtual public Base1 // 虚继承
{
public:
int var2;
};
// 直接基类
class Base3 : virtual public Base1 // 虚继承
{
public:
int var3;
};
// 派生类
class Derive : public Base2, public Base3
{
public:
void set_var1(int tmp) { var1 = tmp; }
void set_var2(int tmp) { var2 = tmp; }
void set_var3(int tmp) { var3 = tmp; }
void set_var4(int tmp) { var4 = tmp; }
private:
int var4;
};
int main()
{
Derive d;
return 0;
}
在上述例子的整个继承关系中,直接或间接继承虚基类的所有派生类,都是默认构造函数,若虚基类到构造函数带形参,就必须在所有派生类的构造函数的成员初始化列表中列出对虚基类的初始化。
#include <iostream>
using namespace std;
// 间接基类,即虚基类
class Base1
{
public:
Base1(int v) : var1(v){}
int var1;
};
// 直接基类
class Base2 : virtual public Base1 // 虚继承
{
public:
Base2(int v) : Base1(v){}
int var2;
};
// 直接基类
class Base3 : virtual public Base1 // 虚继承
{
public:
Base3(int v) : Base1(v){}
int var3;
};
// 派生类
class Derive : public Base2, public Base3
{
public:
Derive(int v) : Base1(v), Base2(v), Base3(v){}
void set_var1(int tmp) { var1 = tmp; }
void set_var2(int tmp) { var2 = tmp; }
void set_var3(int tmp) { var3 = tmp; }
void set_var4(int tmp) { var4 = tmp; }
private:
int var4;
};
int main()
{
Derive d;
return 0;
}
对于从虚基类继承过来的数据成员var1是否初始化了3次?
只初始化了1次。C++编译器的解决方法:将建立对象时所指定的类称为当时的最远派生类,则Derive就是最远派生类。建立一个对象时,如果这个对象中含有从虚基类继承过来的成员,则虚基类的成员是由最远派生类的构造函数通过调用虚基类的构造函数进行初始化的,该派生类的其他基类(Base2,Base3)对虚基类构造函数的调用都自动被忽略。