菱形继承与虚拟继承

本文详细介绍了继承机制,包括继承方式、构造函数和析构函数的调用顺序、同名隐藏规则、赋值兼容规则、友元函数和静态成员的继承。重点讨论了菱形继承的问题,并提出虚继承作为解决方案,强调了虚函数的声明和使用注意事项。

1.继承

1>继承机制:是为了扩展原有类,增加新的功能

2>继承的定义格式:

子类名:继承方式    父类名

3>继承方式有三种:private(私有继承) protected(保护继承) public(公有继承); 

    公有继承:基类中公有成员和保护成员在派生类中的访问权限不发生改变,基类中的私有成员在派生中是不可访问

的;

    保护继承:基类中的公有成员和保护成员在派生类中都将修改为保护成员,基类中的私有成员在派生类中是不可访

的; 

    私有继承:基类中的公有成员和保护成员在派生类中都将修改为私有成员,基类中的私有成员在派生类中是不可访

的。

2.继承中构造函数和析构函数的调用顺序

当基类中显式定义构造函数,派生类中也显式定义了构造函数: 

构造函数的调用顺序:先调派生类的构造函数,在派生类的初始化列表中调基类的构造函数;

析构函数的调用顺序:先调派生类的析构函数(将派生类的析构函数体的内容执行完毕,在将要结束派生类的析构函

数时调用基类的析构函数,将基类的析构函数调用完成后会返回到派生类析构函数体的左花括号之前,继而结束派生

类的析构函数)。

3、总结:

①基类使用系统合成的构造函数(只有在需要时系统才会合成),这时的派生类也可以使用系统合成的构造函数(也

是在需要时才会合成)。 

当基类显式定义了缺省的构造函数,这时的派生类可以使用系统合成的(系统会在此时合成派生类的构造函数,在派

生类的初始化列表会去调基类的构造函数)。

②当基类中定义了非缺省的构造函数(需要参数),这时派生类中必须显式定义构造函数,且在派生类构造函数的初

始化列表需要调用基类的构造函数(传参),这里应该很容易明白,因为系统并不知道该给基类的构造函数传什么参

数,所以你必须自己进行传参。

③基类通常都应该定义一个虚析构函数,即使该函数不执行任何实际操作也是如此。

④基类的私有成员在派生类中是不可访问的,如果基类不想在类外直接访问,但又想在派生类中可以访问时,需要定

义为protected。可以看出,protected这个访问权限是为了继承才出现的。

⑤派生类会继承基类中的所有成员,只是访问权限有时会发生改变,导致基类的某些成员在派生类中不可见。

⑥每个类控制他自己的成员初始化过程。

使用关键字struct时—>默认公有继承 使用关键字class时—–>默认私有继承

公有继承是一个接口继承,每个子类对象都可以看成是一个父类对象。

4.同名隐藏

当派生类中特有的成员和从基类所继承的成员同名(包括成员变量和成员函数)时,在派生类中会隐藏基类中的成

员,即使用派生类的对象直接只能访问派生类中的(和基类同名)成员,但是有时候也需要可以访问基类中的成员

(同名),这时在派生类的成员函数可以使用形如 : 基类类名::成员名来访问基类中的同名成员。

①注意:当父类和子类成员函数同名时,这时只需关注函数名,与参数列表和函数的返回值都无关;当参数列

表不同时,可能会有同学认为它们可以构成重载,切记:它们不能构成重载,可以构成重载函数的首要条件是必须在

同一作用域内,这里的父类和子类很明显是两个作用域。

②一般在继承体系中最好不要使用同名成员。

5.赋值兼容规则(public继承): 

①子类对象可以赋值给父类对象(子类对象也可以看成一个父类对象); 

②父类对象不能赋值给子类对象;

③子类对象的指针/引用不可以指向父类对象。(强制类型转换可以完成);父类对象的指针/引用不可以指向弗雷对

象。


6. 
友元函数不能继承(因为友元函数不属于类,还记得友元函数不受类中访问权限的限制)。

静态成员变量可以继承,而且整个继承体系中只有一份静态成员变量,是所有类对象所共享的。

7.单继承多继承菱形继承***菱形虚拟继承

①单继承:一个子类只有一个直接父类时的这种继承关系为单继承。
 

②多继承:一个子类有两个或两个以上父类时的这种继承关系成为多继承。 

多重继承的派生类继承了所有基类的属性。 

③菱形继承(钻石继承)

菱形继承会造成二义性和数据冗余的问题。为了解决这个问题,我们引入了虚继承。

④虚继承(为了解决菱形继承造成的二义性和数据冗余等问题) 

虚继承的目的是令某个类做出声明,承诺愿意共享它的基类。1

注意:虚继承和一般的继承在调用构造函数时有一点差别,虚拟继承会先将1压栈,然后调用构造函数,而一般的继

承直接调用构造函数,可以看出将1压栈只是为了区分虚拟继承和一般的继承。

设置虚函数须注意: 

1>只有类的成员函数才能说明为虚函数; 

2>静态成员函数不能是虚函数; 

3>内联函数不能为虚函数; 

4>构造函数不能是虚函数; 

5>析构函数可以是虚函数,而且通常声明为虚函数。

看一个例子:

class B
{
public:
	void Print()
	{
		cout << _b << endl;//_b 最后被赋值20
	}
	int _b;
};//4
class C1 :virtual public B
{
public:
	int _c1;
};//12
class C2 :virtual public B
{
public:
	int _c2;
};//12
class D :public C1, public C2
{
public:
	int _d;
};
int main()
{
	cout <<"D:"<< sizeof(D) << endl;
	D d;
	d._c1 = 1;
	d._c2 = 2;
	d._d = 3;
	C1& c1 = d;
	c1._b = 10;
	C2& c2 = d;
	c2._b = 20;
	d.Print();
	return 0;
}


运行结果:



评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值