转载自“https://blog.csdn.net/wu_123_456/article/details/24418841”
1.共同基类派生产生的二义性及解决办法?
-
<span style=
"font-size:14px;">#include <iostream>
-
using
namespace
std;
-
-
class A //公共基类
-
{
-
protected:
//protected成员列表
-
int x;
-
-
public:
//public成员列表
-
A(
int xp =
0)
//构造函数
-
{
-
x = xp;
-
}
-
-
void SetX(int xp) //设置protected成员x的值
-
{
-
x = xp;
-
}
-
-
void print()
-
{
-
cout <<
"this is x in A: " << x <<
endl;
-
}
-
};
-
-
//class B: virtual public A
-
class B:
public A
//类B由类A派生而来
-
{
-
};
-
-
//class C: virtual public A //类C由类A派生而来
-
class C:
public A
//类C由类A派生而来
-
{
-
};
-
-
class D :
public B,
public C
//类D由类B和类C派生而来
-
{
-
};
-
-
int main()
-
{
-
D d;
//声明一个D类对象exD
-
d.SetX(
5);
//SetX()具有二义性, 系统不知道是调用B类的还是C类的SetX()函数
-
d.print();
//print()具有二义性, 系统不知道是调用B类的还是C类的print()函数
-
return
0;
-
}</span>
解决办法:
使用关键字virtual将共同基类A声明为虚基类,例如class B:virtual Public A{}; class C:virtual Public A{};
D中对于这两个函数只有一个备份,也可通过上次说的成员名限定,d.A::print();d.B::print();d.C::print();
2.虚基派生二义性与多基派生二义性不同?
(1.)多基派生说白了就是不同基类里面有相同的函数名,即函数名的二义性,通过加域名限定符来解决。
(2.)虚基派生是同一基类的多重拷贝,即存储的二义性,通过加virtual来解决。
3.虚基派生的构造函数的调用关系。
-
<span style=
"font-size:12px;">#include <iostream>
-
using
namespace
std;
-
-
class A //类A定义
-
{
-
private:
//private成员列表
-
int x;
-
-
public:
//public成员列表
-
A(
int xp =
0)
//构造函数,带缺省参数
-
{
-
x=xp;
-
cout<<
"A的构造函数被执行"<<
endl;
-
}
-
-
~A()
//析构函数
-
{
-
cout<<
"A的析构函数被执行"<<
endl;
-
}
-
-
void Print() //显示成员变量x的值
-
{
-
cout << x <<
endl;
-
}
-
};
-
-
class B:
virtual
public A
//类B由类A虚基派生而来
-
{
-
public:
-
B(
int xp):A(xp)
//在初始化表中调用基类构造函数
-
{
-
cout<<
"B的构造函数被执行"<<
endl;
-
}
-
-
~B()
//析构函数
-
{
-
cout<<
"B的析构函数被执行"<<
endl;
-
}
-
};
-
-
class C:
virtual
public A
//类C由类A虚基派生而来
-
{
-
public:
-
C(
int xp):A(xp)
//在初始化表中调用基类构造函数
-
{
-
cout<<
"C的构造函数被执行"<<
endl;
-
}
-
-
~C()
//析构函数
-
{
-
cout<<
"C的析构函数被执行"<<
endl;
-
}
-
};
-
-
class D:
public B,
public C
//类D由类B和类C共同派生而来
-
{
-
public:
-
D(
int xp):B(xp),C(xp),A(xp)
//初始化表中不仅要调用B类和C类的构造函数,还应调用共同虚基类的构造函数A(xp)
-
{
-
cout<<
"D的构造函数被执行"<<
endl;
-
}
-
-
~D()
//析构函数
-
{
-
cout<<
"D的析构函数被执行"<<
endl;
-
}
-
};
-
-
int main()
-
{
-
D d(2);
//声明D类对象d
-
d.Print();
//结果为2。如果去掉类D的构造函数的初始化列表中的A(xp),则结果为0。好好体会!!!!!!!!
-
-
cout <<
endl;
-
B b(3);
-
b.Print();
//结果为3。如果去掉类B的构造函数的初始化列表中的A(xp),则结果为0。好好体会!!!!!!!!
-
cout <<
endl;
-
-
return
0;
//main函数执行完毕退出后,d销毁,析构函数触发执行
-
}</span>
大家可能会问:
(1.)定义一个D的对象,岂不是A的构造函数要调用两次?
(2.)d.Print(); //结果为2。如果去掉类D的构造函数的初始化列表中的A(xp),则结果为0。这是为什么?
【解析】:
实际情况是:
B(总参数表):A(参数表)
C(总参数表):A(参数表)
D(总参数表):B(参数表),C(参数表),A(参数表)
根据虚基派生的性质,类D中只有一份虚基类A的拷贝,因此A类的构造函数在D类中只能被调用一次。所以,从A类直接派生(B和C)和间接派生(D)的类中,其构造函数的初始化列表中都要列出对虚基类A构造函数的调用。这种机制保证了不管有多少层继承,虚基类的构造函数必须且只能被调用一次。
4.继承和组合的区别?贴一段组合的代码,一看就明白了。
-
#include <iostream>
-
using
namespace
std;
-
-
class Eye
-
{
-
public:
-
void Look() {
cout <<
"Eye.Look()." <<
endl;}
-
};
-
-
class Nose
-
{
-
public:
-
void Smell() {
cout <<
"Nose.Smell()." <<
endl;}
-
};
-
-
class Mouth
-
{
-
public:
-
void Eat() {
cout <<
"Mouth.Eat()." <<
endl;}
-
};
-
-
class Ear
-
{
-
public:
-
void Listen() {
cout <<
"Ear.Listen()." <<
endl;}
-
};
-
-
//组合方式:逻辑很清晰,后续扩展很方便。
-
class Head
-
{
-
private:
-
Eye m_eye;
-
Nose m_nose;
-
Mouth m_mouth;
-
Ear m_ear;
-
-
public:
-
void Look()
-
{
-
m_eye.Look();
-
}
-
-
void Smell()
-
{
-
m_nose.Smell();
-
}
-
-
void Eat()
-
{
-
m_mouth.Eat();
-
}
-
-
void Listen()
-
{
-
m_ear.Listen();
-
}
-
};