C++笔记-继承(包含派生类的默认成员函数,菱形继承等)
派生类的拷贝构造函数必须调用基类的拷贝构造完成基类的拷贝初始化。
3.派生类的operator=必须要调用基类的operator=完成基类的复制。需要注意的是派生类的operator=隐藏了基类的operator=,所以显示调用基类的operator=,需要指定基类作用域 4.派生类的析构函数会在被调用完成后自动调用基类的析构函数清理基类成员。因为这样才能保证派生类对象先清理派生类成员再清理基类成员的顺序。 5.派生类对象初始化先调用基类构造再调派生类构造。 6.派生类对象析构清理先调用派生类析构再调基类的析构。 7.因为多态中一些场景析构函数需要构成重写,重写的条件之一是函数名相同。那么编译器会对析构函数名进行特殊处理,处理成destructor(),所以基类析构函数不加 virtual的情况下,派生类析构函数和基类析构函数构成隐藏关系。

首先先写一个父类Person,便于下面的讲解。
在写子类的4个默认函数时要把父类成员当成一个整体
首先将其中的构造函数:

正如第一条所言,我们在写子类的构造函数时,在初始化列表部分要显示调用父类的构造函数,这里显式调用的格式就如上图所示,如果父类还有其他的成员变量,那么一并都写入父类的构造函数种。

接下来呢时拷贝构造函数,这类有人可能有疑惑:为什么直接传s就可以呢? 这个就涉及到我们上一篇讲的基类和派生类之间的转化,有印象的朋友就能记起来子类对象可以赋值给父类的引用或指针,本质就是“切片”,而我们上面写的Person类中的拷贝构造函数正是引用,所以这里直接传子类对象即可。

接着是=符号重载,这里面要注意的是我们要指定作用域来调用Person类中的=符号重载,至于原因也和上一篇讲的知识有关:最后讲的隐藏规则。
这里如果不指定作用域,那么子类的=符号重载会将父类的=符号重载给隐藏,这里会一直调用自己,一直递归,最终导致栈溢出。

而最后的析构函数呢情况比较复杂,就如上图所示,按理说应该没问题的,就和上面的构造函数和拷贝构造函数一样,那么这里为何为报错呢?
原因就正如第七条所言,因为都是destructor,所以子类的析构函数把父类的析构函数给隐藏了,所以这里就找不到父类的析构函数。

所以要利用父类的析构函数就要指定作用域,但是析构函数比较特殊,我们在实际应用中不会去显示调用父类的析构函数,原因就如第六条所言,我们要调用子类的析构函数,再调用父类的析构函数,而这里我们如果在子类析构函数中上去就直接调用父类的析构函数,那么就会使顺序颠倒。
并且因为第四条所言,那么就会导致父类的析构函数被调用两次:

很明显,是不能这么用的,这肯定会出问题的。所以在实际运用中我们是不需要主动去调用父类的析构函数,这也是析构函数和前面三个默认函数的区别。
4.2实现一个不能被继承的类
不能被继承的方式我讲解两种:c++98和c++11两种不同的方式。

c++98的方法就是将父类的构造函数放在private下,这种方式为什么可以呢?
就如上面讲的构造函数,子类构造函数要显示调用父类的构造函数,而父类的构造函数不能访问,通过这种机制就导致父类无法被继承。

当我们创建对象时就会出现这样的报错。

而c++11的方法相较于c++98简单了许多,直接在类名后面加一个关键字final,这样这个类就无法被继承了,就如上图所示。

二.继承和友元
友元关系不能被继承,也就是说基类友元不能访问派生类私有和保护成员。

我们以上面的例子为例,在讲解之前,注意我上面写的前置声明,因为Display函数同时用到了两个类,而student类还没有实现,所以就在前面加上一个前置声明,来告诉编译器我下面有一个类叫student。通过上面的例子可以看出,Display并不能访问子类的保护成员。这就像你父亲的朋友不是你的朋友一样,是一个道理,所以是无法访问的。
更多推荐
所有评论(0)