C/C++日常学习总结(第四篇)共同基类产生的二义性和继承与组合的区别

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


 

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值