C++基础知识,多重继承,虚基类,虚继承

派生类继承构造函数

如果基类含多个构造函数,多数情况下,派生类一般会集成所有构造函数,以下几种情况比较特殊

  • 1、如果你在派生类中定义的构造函数与基类构造函数有相同的参数列表,那么从基类中继承的会被覆盖掉也就是只继承了及部分构造函数
  • 2、拷贝,移动构造函数不会被继承
  • 3、using classA::classA,这不属于自己定义构造函数,编译器还是会合成一个默认的无参构造函数,但是这样声明了之后,父类必须有无参构造函数

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

class classA
{
public:
	//classA(int i, int j, int k){}
	classA(int i, int j, int k = 0){}
	classA() {}
};
class classB:public classA
{
public:
	//classB(int i, int j, int k) :classA(i, j, k){}
	using classA::classA;
};

这样看来,using就是让某个函数名字可见
遇到这条代码是,编译器会把基类中每个构造函数,都生成一个与之对应的构造函数
但是到底长什么样呢?
classB(参数列表):classA(照抄父类参数列表){},比如这个
classB(int i,int j,int k):classA(i,j,k){}

如果有默认参数的话,那么编译器遇到using classA::classA的时候
就会帮我们在派生类中构造出多个构造函数出来

  • 1、带所有参数的构造函数
  • 2、其余分别少一个默认参数
    例如classA(int i, int j, int k = 0)
    如果使用using classA::classA,那么就会有如下的默认构造函数生成
    1、classB(int i , int j , int k):A(i,j,k){}
    2、classB(int i , int j):A(i,j){}

多重集成

class grandFather
{
public:
	int m_gf;
public:
	//这里参数列表赋值
	grandFather(int i) :m_gf(i)
	{
		cout << "grandFather构造函数" << endl;
	}

	void myInfo()
	{
		cout << m_gf << endl;
	}
	virtual ~grandFather()
	{
		cout << "grandFather析构函数" << endl;
	}
};

class father :public grandFather
{
public:
	int m_f;
public:
	//每个子类的构造函数,负责解决自己父类的初始化问题
	//每个类都要解决直接父类的初始化问题
	//不然只会调用默认的无参构造函数
	//想一想,java的双亲委托机制是不是就是这样实现的?
	father(int i) :m_f(i), grandFather(m_f)
	{
		cout << "father构造函数" << endl;
	}
	virtual ~father()
	{
		cout << "father析构函数" << endl;
	}
	void myInfo()
	{
		cout << m_f << endl;
	}
};

class others
{
public:
	int m_o;
public:
	//每个子类的构造函数,负责解决自己父类的初始化问题
	others(int i) :m_o(i) 
	{
		cout << "others构造函数" << endl;
	}
	virtual ~others()
	{
		cout << "others析构函数" << endl;
	}
	void myInfo()
	{
		cout << m_o << endl;
	}
};

//派生列表
class classSon :public father, public others
{
public:
	int m_s;
public:
	//有两个直接父类,都要把他们初始化
	classSon(int i, int j, int k) :father(i), others(j),m_s(k)
	{
		cout << "classSon构造函数" << endl;
	}
	virtual ~classSon()
	{
		cout << "classSon析构函数" << endl;
	}
	//因为父类函数冲突了,子类重写覆盖
	void sonInfo()
	{
		cout << m_s << endl;
	}
	//遇到冲突的问题,可以直接在子类中,进行
	//父类::函数/参数
	void sonCallParentInfo()
	{
		father::myInfo();
		others::myInfo();
		sonInfo();
	}
};
  • 再多重继承中,派生类会包含每个基类的子对象
int main()
{
	classSon class1(1, 2, 3);
	class1.sonInfo();
}

执行结果:
在这里插入图片描述

  • father覆盖了grandFather中的myInfo();如果子类继承的多个类中都有myInfo这个函数,就要显示声明调用的是哪个函数
	classSon class1(1, 2, 3);
	class1.father::myInfo();
	class1.others::myInfo();

在这里插入图片描述

静态成员变量

属于类,不属于对象
但是对象也是可以调用的
在grandFather中加上

class grandFather
{
public:
	static int gf_SI;
}
//为了能够使用,要定义这个静态类
//没有用到就不用定义,使用必须定义,光声明是不行的
int grandFather::gf_SI = 666;

int main()
{
	cout << grandFather::gf_SI << endl;
	cout << father::gf_SI << endl;
	cout << classSon::gf_SI << endl;
	//对象也是可以的
	cout << class1.gf_SI << endl;
}

在这里插入图片描述

派生类构造函数与析构函数

  • 1、构造一个派生类对象,将同时构造初始化所有基类子对象
  • 2、派生类的初始化列表只初始化它的直接基类,这样所有的都能初始化
  • 3、派生类构造函数吃石化列表将实参分别传递给每个直接基类,基类的构造顺序由派生列表决定,派生列表:class classSon :public father, public others,跟初始化列表(classSon(int i, int j, int k) :father(i), others(j),m_s(k))没有关系

显式的初始化与隐士的初始化(使用默认构造函数,不带参数)

//新建了一个类others2,只有无参构造方法
class others2
{
public:
	//是默认的无参构造方法
	others2() 
	{
		cout << "others2构造函数" << endl;
	}

	virtual ~others2()
	{
		cout << "others2析构函数" << endl;
	}
};
class classSon :public father, public others,public others2
{
public:
	int m_s;
public:
	//虽然继承了others2类,但是由于只有无参构造函数,不显式初始化也是可以的
	classSon(int i, int j, int k) :father(i), others(j),m_s(k)
	{}
}

继承的两个构造函数参数重复,会导致程序错误,必须定义自己的版本

class A
{
public:
	A(int val) {};
};
class B
{
public:
	B(int val) {};
};
class C :public A, public B
{
public:
	//这样继承了A,B的构造函数,但是一摸一样(只要参数列表一样就是一摸一样)
	using A::A;
	using B::B;
	//必须定义自己的参数列表一样的去覆盖
	C(int val) :A(val), B(val) {}
};

解释为什么基类指针可以指向子类对象

基类指针了一指向派生类对象,编译器会帮助我们进行转换,因为每个派生类对象包含一个基类对象区域,所以基类的指针和引用可以绑定到基类上

	grandFather* g = new classSon(1, 2, 3);

虚基类,虚继承

派生列表中同一类只能出现一次,但是以下情况特别

  • 1、通过两个直接基类,继承了两次间接基类
  • 2、直接继承该基类,通过直接继承另一个类,又间接继承了该类

初始化多次,名字冲突,不能直接调用那个类的方法,因为不知道用哪一个的
如下:
我们重写others类,让它也和father一样继承grandfather类

class others:public grandFather
{
public:
	int m_o;
public:
	//初始化grandFather
	others(int i) :m_o(i) ,grandFather(i)
	{
		cout << "others构造函数" << endl;
	}
	virtual ~others()
	{
		cout << "others析构函数" << endl;
	}
	void myInfo()
	{
		cout << m_o << endl;
	}
};

int main()
{
	classSon class1(1,2,3);
}

然后classSon类实际上通过father,others类继承了两次grandfather类,看一看初始化classSon类的效果
真的构造了两次,不行不行
在这里插入图片描述
解决方法:虚继承
首先,虚基类:无论这个类在继承体系中出现多少次,派生类中都只会包含唯一一个虚基类子内容
这种虚继承不对直接子类有意义,只对直接子类派生出来的类才有意义
最后所有虚基类的子类都要虚继承,才能保证孙类是虚继承
格式:class classA:virtual father,并且要用孙类的构造函数进行初始化

class classSon : public father, public others
{
public:
	//如果父类是虚继承,那么初始化虚基类的任务就要交给最小辈儿的类来
	classSon(int i, int j, int k) :grandFather(k),father(i), others(j),m_s(k)
	{cout << "classSon构造函数" << endl;}
}


//			声明虚继承
class others:virtual public grandFather
{
public:
//						这里初始化也没什么用了
	others(int i) :m_o(i) ,grandFather(i)
}


//			声明虚继承
class father :virtual public grandFather
{
public:
	//						这里初始化也没什么用了
	father(int i) :m_f(i), grandFather(m_f)
}

int main()
{
	classSon class1(1,2,3);
}

执行效果:
在这里插入图片描述

说明两点:

  • 1、现在是孙子类构造函数初始化,如果以后它再有子类,那么也要进行加上换句话说,就是最底层的进行初始化虚基类
  • 2、虚基类不管派生列表的顺序,它就是首先进行初始化
  • 3、多个虚基类,从派生列表的继承顺序,往回追溯,先找到那个虚基类,就先初始化哪个

总结:

  • 小心虚继承,不太提倡使用
  • 简单,不易出现二义性,实在必要才用。
发布了157 篇原创文章 · 获赞 167 · 访问量 2万+

猜你喜欢

转载自blog.csdn.net/qq_40666620/article/details/102973354