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();
}
};