14.15 继承的构造函数、多重继承、类型转换与虚继承

一:继承的构造函数

一个类只继承其直接基类(父类)的构造函数,默认,拷贝,移动构造函数是不能被继承的。

如果基类含有多个构造函数,则多数情况下,派生类会继承所有这些构造函数,但如下情况例外:
<1>如果你在派生类中定义的构造函数与基类构造函数有相同的参数列表,那么从基类中继承来的构造函数会被你在派生类中的定义覆盖掉
(相当于你只继承一部分基类中的构造函数)。
<2>默认,拷贝,移动构造函数不会被继承。

class MA
{
public:
    MA() {};
    MA(int i, int j, int k = 5)
    {
    };
};

class MB : public MA
{
public:
    //MB(int i, int j, int k):MA(i,j,k){}
    using MA::MA;  // 继承MA的构造函数,using::让某个名字在当前的作用域可见
                   // 遇到这条代码的时候,会把基类的每个构造函数,都生成一个与之对应的派生类构造函数。
                   // MB(构造函数形参列表...) : MA(照抄的构造函数形参列表){};  函数体为空
                   // MB(int i, int j, int k):MA(i,j,k){}

    // 如果基类MA的构造函数有默认参数的话,那么编译器遇到这种using MA::MA的时候,就会帮我们在派生类MB中构造出多个构造函数。
    // (a)第一个构造函数是带有所有参数的构造函数
    // (b)其余的构造函数,每个分别忽略掉一个默认参数
    // MB(int i, int j, int k):MA(i,j,k){}
    // MB(int i, int j):MA(i,j){}
    // 如果类MB,只含有using MA::MA 从MA继承来的构造函数的话,那么编译器是会给它合成默认的构造函数的。
};

MB ad(3, 4, 5);
MB ad(3, 4);
MB ef;

二:多重继承

<1>多重继承概述
单继承, 如果从多个父类来产生出子类:多重继承

MC ctest(10, 20, 50);
ctest.myinfoC();
ctest.myinfo();  //要增加作用域,明确的告诉系统调用的是类A还是父类B的成员函数。
ctest.MA::myinfo();

如果在自己的类中定义了myinfo,则就会覆盖掉父类中的同名的函数。派生类会包含每个基类的子对象。

<2>静态成员变量
静态成员属于类,不属于对象

Grand::m_static = 1;  //可以用类名来引用静态变量
MA::m_static = 2;
//MB::m_static = 5;
MC::m_static = 4;
ctest.m_static = 19;  //可以用对象名来引用静态变量

<3>派生类构造函数与析构函数
1>构造一个派生类对象将同时构造并初始化所有的基类子对象。
2>派生类的构造函数初始化列表只初始化它的直接基类,每个类的构造函数都负责初始化它的直接基类,就会让所有类都得到初始化。
3>派生类构造函数初始化列表将实参分别传递给每个直接基类,基类的构造顺序跟"派生列表"中基类的出现顺序保持一致。
概念:显示的初始化基类和隐式的初始化基类问题
MC ctest(10, 20, 50);

<4>从多个父类继承构造函数
子类要定义同参数的构造函数的自己的版本。

三:类型转换

基类指针可以指向一个派生类对象:编译器帮助我们隐式执行这种派生类到基类的转换,转换成功的原因是每个派生类对象都包含一个基类对象部分。
所以基类的引用或者指针是可以绑到到基类对象这部分上来的。

Grand* pg = new MC(1, 2, 3);
MA* pa = new MC(1, 2, 3);
MB* pb = new MC(4, 5, 6);
MC myc(6, 7, 8);
Grand mygrand(myc);  //回顾一下三章十一节

四:虚基类(virtual base class),虚继承(虚派生)

派生列表中,同一个基类只能出现一次,但是如下两种情况例外:
<1>派生类可以通过它的两个直接基类分别继承同一个间接基类;
<2>直接继承某个基类,然后通过另一个基类间接继承该类。
这样导致Grand被构造了两次。继承2次Grand是多余的,占空间,还可能产生名字冲突。
MC ctest(10, 20, 50);
ctest.m_valuegrand = 18;
虚基类:无论这个类在继承中出现多少次,派生类中,都只会包含唯一一个共享的虚基类(Grand)子内容。
大家观察到一个现象,这种继承只对MC类有意义,其实对于MC的父类MA,MA2没有意义;
换句话说,MA、MA2从Grand类虚继承,只影响从MA、MA2这些类中进一步派生出的类MC,而对MA、MA2本身没有什么影响。
每个Grand的子类都要虚继承Grand类,那么才能保证Grand的孙类能够虚继承Grand类。
只要子类中都加virtual继承,那么Grand类自然成为了"虚基类"。
virtual:表达一种意愿:表示后续从我(类MA,MA2)派生的类中应该共享虚基类Grand的同一份实例。
共享一份数据。

特别说明:
<1>现在是MC类初始化Grand类,如果以后MC类有了孩子,则有MC类的子类初始化Grand类,换句话说:虚基类Grand由最低层的派生类来初始化,现在MC是最低层。
(2)初始化顺序问题:先初始化虚基类部分,然后再按照派生列表中出现的顺序来初始化其他类。
(3)多个虚基类,哪个虚基类先初始化呢? 按照派生列表中的直接基类往回追溯,看是否这些直接基类含有虚基类,反正是追溯到哪个虚基类,
那就先构造哪个虚基类的子内容;先追溯到哪个先初始化哪个。
销毁和构造按顺序正好相反

MC ctest(10, 20, 50);
ctest.m_valuegrand = 18; //统一成员变量,父类会覆盖爷爷类

一旦Grand成为虚基类之后,Grand类的初始化工作就不会再由它的直接子类MA、MA2来初始化了。
MA(int i) :Grand(i), m_valuea(i)
MA2(int i) :Grand(i), m_valuea2(i)
但也不能删除子类MA、MA2中Grand,否则编译时会报语法错误。

总结
小心虚继承,不太提倡使用。
简单,不容易出现二义性,实在必要才使用多继承,能用单一继承解决的问题就不要用多重继承了。

class Grand  //爷爷类,若子类均定义为virtual,那么父类自动转换为virtual
{
public:
	//Grand()
	//{
	//}
	Grand(int i) :m_valuegrand(i)
	{
		cout << "Grand构造函数执行" << endl;
	}
	virtual ~Grand()
	{
		cout << "Grand析构函数执行" << endl;
	}
	void myinfo()
	{
		cout << m_valuegrand << endl;
	}

public:
	int m_valuegrand;
	//static int m_static;  //声明一个静态成员变量
};

// 为了能够使用,我们定义这个静态成员变量(分配内存)
//int Grand::m_static = 5;  // 如果你在代码中不用m_static,那么可以不定义。如果代码中用到m_static,则必须定义,否则连接出错。

//定义父类A
//class MA : public Grand  //继承自爷爷类
class MA : virtual public Grand  //类A从Grand虚继承 virtual的位置和public可以调换
{
public:
	MA(int i) :Grand(i), m_valuea(i)  //每个子类的构造函数,负责解决自己父类的初始化问题
	{
		cout << "MA构造函数执行" << endl;
	}
	virtual ~MA()
	{
		cout << "MA析构函数执行" << endl;
	}
	void myinfo()
	{
		cout << m_valuea << endl;
	}

public:
	int m_valuea;
	int m_valuegrand;
	//int m_static;
};

//class MA2 : public Grand
class MA2 : public virtual Grand  //虚继承Grand类
{
public:
	MA2(int i) :Grand(i), m_valuea2(i)  //每个子类的构造函数,负责解决自己父类的初始化问题。
	{
		cout << "MA2构造函数" << endl;
	}
	virtual ~MA2()
	{
		cout << "MA2析构函数" << endl;
	}
	void myinfo()
	{
		cout << m_valuea2 << endl;
	}

public:
	int m_valuea2;
	//int m_static;
};

//定义父类B
class MB  //不继承自Grand
{
public:
	MB() //默认构造函数
	{
		cout << "MB默认构造函数执行" << endl;
	}
	MB(int i) :m_valueb(i)
	{
		cout << "MB构造函数执行" << endl;
	}
	virtual ~MB()
	{
		cout << "MB析构函数执行" << endl;
	}
	void myinfo()
	{
		cout << m_valueb << endl;
	}

public:
	int m_valueb;
	int m_valuegrand;
};

//c语言没有明确规定可以有多少个父类
//class MC : public MA, public MB  //如果是默认继承(不写public), 看MC是struct还是class,如果C是class则默认是private
//struct MC :MA, MB              //如果MC是struct,则默认是public继承
class MC :public MB, public MA, public MA2
{
public:
	//MC(int i, int j, int k) :MA(i), MA2(i), m_valuec(k)  //隐式的使用MB的默认构造函数(不带参数的构造函数)来初始化MB的子对象。
	MC(int i, int j, int k) :MA(i), MA2(i), MB(j), Grand(i), m_valuec(k) //虚基类时,孙子MC来初始化爷爷Grand。
	{
		cout << "MC构造函数执行" << endl;
	}
	//MC(int i, int j, int k) :MA(i), MA2(i), m_valuec(k) { cout << "MC构造函数" << endl; };
	//MC(int i, int j, int k) :MA(i), MA2(i), Grand(i), m_valuec(k) { cout << "MC构造函数" << endl; };
	virtual ~MC()
	{
		cout << "MC析构函数执行" << endl;
	}
	void myinfoC()
	{
		cout << m_valuec << endl;
		MA::myinfo();  //调用父类A的myinfo函数
		MB::myinfo();  //调用父类B的myinfo函数
	}
	void myinfo() {};

public:
	int m_valuec;
	int m_valuegrand;
};
  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值