C++继承详解

公有继承(public)、私有继承(private)、保护继承(protected)是常用的三种继承方式。

1. 公有继承(public)

公有继承的特点是基类的公有成员和保护成员作为派生类的成员时,它们都保持原有的状态,而基类的私有成员仍然是私有的,不能被这个派生类的子类所访问。

2. 私有继承(private)

私有继承的特点是基类的公有成员和保护成员都作为派生类的私有成员,并且不能被这个派生类的子类所访问。

3. 保护继承(protected)

保护继承的特点是基类的所有公有成员和保护成员都成为派生类的保护成员,并且只能被它的派生类成员函数或友元访问,基类的私有成员仍然是私有的。

下面列出三种不同的继承方式的基类特性和派生类特性。

C++继承详解_继承


在上图中:1)基类成员对派生类都是:共有和保护的成员是可见的,私有的的成员是不可见的。

                  2)基类成员对派生类的对象来说:要看基类的成员在派生类中变成了什么类型的成员。如:私有继承时,基类的共有成员和私有成员都变成了派生类中的私有成员,因此对于派生类中的对象来说基类的共有成员和私有成员就是不可见的。

 为了进一步理解三种不同的继承方式在其成员的可见性方面的区别,下面从三种不同角度进行讨论。

对于公有继承方式

(1) 基类成员对其对象的可见性:

公有成员可见,其他不可见。这里保护成员同于私有成员。

(2) 基类成员对派生类的可见性:

公有成员和保护成员可见,而私有成员不可见。这里保护成员同于公有成员。

(3) 基类成员对派生类对象的可见性:

公有成员可见,其他成员不可见。

所以,在公有继承时,派生类的对象可以访问基类中的公有成员;派生类的成员函数可以访问基类中的公有成员和保护成员。这里,一定要区分清楚派生类的对象和派生类中的成员函数对基类的访问是不同的。

对于私有继承方式

(1) 基类成员对其对象的可见性:

公有成员可见,其他成员不可见。

(2) 基类成员对派生类的可见性:

公有成员和保护成员是可见的,而私有成员是不可见的。

(3) 基类成员对派生类对象的可见性:

所有成员都是不可见的。

所以,在私有继承时,基类的成员只能由直接派生类访问,而无法再往下继承。

对于保护继承方式

这种继承方式与私有继承方式的情况相同。两者的区别仅在于对派生类的成员而言,对基类成员有不同的可见性。

上述所说的可见性也就是可访问性。

关于可访问性还有另的一种说法。这种规则中,称派生类的对象对基类访问为水平访问,称派生类的派生类对基类的访问为垂直访问。


=========================================================

详细讲解C++ 类的继承

一个私有的或保护的派生类不是子类,因为非公共的派生类不能做基类能做的所有的事,就是指在公开场合,但是在类内部可以的

一、引言

在C++中,类是提供封装的逻辑单位,类的每一个对象都包含有描述其自身状态的数据集合,并且通过接收特定的消息来处理这个数据集合。如果程序设计人员能够通过增加、修改或替换指定类的部分内容的方法对该类进行剪裁,就可以适应不同的应用,从而在很大程度上增强了数据封装的价值,而接下来要讨论的继承就完全可以实现这种操作。

二、与继承有关的基本概念

继承是一个进程,通过继承,一个对象可以获得另一个对象的属性(包括函数),并可向其中加入属于自己的一些特征。作为C++语言的一种重要机制,用继承的方法可以自动为一个类提供来自另一个类的操作和数据结构,进而使程序设计人员在一个一般的类的基础上很快建立一个新的类,而不必从零开始设计每个类。

当一个类被其他的类继承时,被继承的类称为基类(可不是鸡肋^_^),又称为父类。

继承其他类属性的类称为派生类,又称为子类。

一般情况下,继承的进程起源于一个基类的定义,基类定义了其所有派生类的公有属性。从本质上讲,基类具有同一类集合中的公共属性,派生类继承了这些属性,并且增加了自己特有的属性。从任何已存在的类继承的实质就是建造新的派生类。

三、单重继承、多重继承与继承链

从一个基类派生的继承称为单继承,换句话说,派生类只有一个直接基类。单继承声明语句的常用格式为:

class 派生类名: 访问控制关键字基类名
{
 数据成员和成员函数声明
};

与此相对地,从多个基类派生的继承称为多继承或多重继承,也就是说,一个派生类有多个直接基类。在某些面向对象的语言(如Java)中不支持类间的多重继承而只支持单重继承,即一个类至多只能有一个直接父类,因此实现类似的功能需要借助接口等其他机制。而在C++中提供了多重继承的语法支持,使得问题变得简单了许多。多重继承声明语句的常用格式为:

class 派生类名: 访问控制关键字基类名1, 访问控制关键字 基类名2,...
{
 数据成员和成员函数声明
};

除了多重继承之外,一个派生类继承多个基类还有一种方法,就是把派生类作为基类再次供别的类继承,产生多层次的继承关系。例如类A派生类B,类B派生类C,则称类A是类B的直接基类,类B是类C的直接基类,类A是类C的间接基类。类的层次结构也叫做继承链。还是上面的例子,当建立类C的对象时,类A 的构造函数最先被调用,接下来被调用的是类B的构造函数,最后是类C的构造函数。析构函数的调用顺序正好相反。当一个派生类继承有层次的类时,继承链上的每个派生类必须将它需要的变量传递给它的基类。

四、公有派生和私有派生

在继承声明语句中,访问控制关键字用于说明在基类定义中所声明的成员和成员函数能够在多大范围内被派生类所访问。访问控制关键字可为public, private或protected。如果访问控制关键字为public,则称派生类从基类公有继承,也称公有派生。如果访问控制关键字为 private,则称派生类从基类私有继承,也称私有派生。现在笔者将公有继承和私有继承的具体区别列表如下。

通过上表,我们可以将两种派生的特点总结如下:

C++继承详解_继承_02


(1)无论哪种派生方式,基类中的private成员在派生类中都是不可见的。也就是说,基类中的private成员不允许外部函数或派生类中的任何成员访问。

(2)public派生与private派生的不同点在于基类中的public成员在派生类中的访问属性:
public派生时,基类中的public成员相当于派生类中的public成员。
private派生时, 基类中的public成员相当于派生类中的private成员。

因此,private派生确保基类中的方法只能被派生类的对象的方法间接使用,而不能被外部使用。public派生使派生类对象与外部都可以直接使用基类中的方法,除非这些方法已经被重新定义。

五、保护成员与保护派生

如果想做到基类成员只由有派生血缘关系的成员访问,而不被无血缘关系的对象成员访问,无论用公有派生还是私有派生都无法做到。因为基类成员中的私有成员是别的类(包括派生类)成员不能访问的,而基类中的公有成员在public派生时,不仅可以由派生类对象成员访问,也可以由外部函数访问;而在 private派生时,基类中的公有成员虽然允许派生类对象中的成员访问,不允许外部访问,可是再派生出下一级时,由于基类的所有成员已经被私有化,其它类成员也不可再访问。实现只许有派生血缘关系的对象成员访问的方法,是在基类中使用具有另一种访问属性的成员——protected成员。

protected成员是一种血缘关系内外有别的成员。它对派生对象而言,是公有成员,可以访问;对血缘关系外部而言,与私有成员一样被隐藏。

此外,除了允许使用private与public两种派生方式之外,C++还允许使用protected派生方式。现在将三种访问属性不同的成员经三种派生后在派生类中访问属性的变化情况总结如下表,是对上一表格的增进和补充。

C++继承详解_继承_03

需要注意的是,基类的private成员无论经过何种派生,在派生类中都是不可见的。

六、友元类和友元函数

(1)友元函数

通常,类的私有成员只能由本类的成员访问,外部函数只能访问类的成员函数,再由成员函数访问类的私有成员。但是,如果在某个类定义中用friend 声明了一个外部函数(也许是其他类的一个成员)后,这个外部函数便可以例外地访问该类的任何私有成员。用friend声明了的外部函数称为这个类的友元函数。

当友元函数是另一个类的成员函数时,应当注意以下几点:

A:友元函数作为一个类的成员函数时,除应当在它所在的类定义中声明之外,还应当在另一个类中声明它的友元关系,声明语句的格式为:

friend 函数类型 函数所在类名::函数名(参数列表);

B:友元函数在引用本类对象的私有成员时无需本类对象的引用参数,但在引用生命它是友元的类的对象中的私有成员时必须有友元类对象的引用参数。

C:一个类的成员函数作另一个类的友元函数时,必须先定义,而不是仅仅声明它。

使用友元函数直接访问对象的私有成员,可以免去再调用类的成员函数所需的开销。同时,友元函数作为类的一个接口,对已经设计好的类,只要增加一条声明语句,便可以使用外部函数来补充它的功能,或架起不同类对象之间联系的桥梁。然而,它同时也破坏了对象封装与信息隐藏,使用时需要谨慎小心。

(2)友元类

也可以把一个类而不仅仅是一个函数声明为另一个类的友元类。这时,只需先声明它而不一定需要先定义。

应当注意,友元关系是单向的,并且只在两个类之间有效。即使类X是类Y的友元,类Y是否是类X的友元也要看类X中是否有相应的声明。即友元关系不具有交换性。若类X是类Y的友元,类Y是类Z的友元,也不一定就说明类X是类Z的友元,即友元关系也不具有传递性。

当一个类要和另一个类协同工作时,使一个类成为另一个类的友元类是很有用的。这时友元类中的每一个成员函数都成为了对方的友元函数。