Inheritance继承

来源:互联网 发布:java程序员考证 编辑:程序博客网 时间:2024/05/17 22:08

Inheritance继承:

1、介绍——何谓继承:

在CPP中,一个类“继承”了另一个类,说明这个类可以使用“被继承”类的共有成员和受保护成员。
直观地讲,就是一个类继承了另一个类的一些成员。
被继承的类称为“超类”、“父类”、“基类”。
继承的类称为“子类”、“派生类”。
定义:“类B继承类A”或“类A派生类B”。
在类B中除了自己定义的成员之外,还自动包括了类A中定义的数据成员与成员函数,这些自动继承下来的成员称为类B的继承成员。

2、好处——为什么要有“继承”:

1、使得程序可刻划现实世界的IS-A关系。如Benz is a car.汽车car是一种大类,而Benz是汽车中的一个小类。Benz有car应该有的,因此可以说Benz继承了car。

2、派生类重用基类类的代码可提高程序开发效率。 派生类的定义通常基于设计完善、并经严格测试的基类,从而使程序设计工作建立在一个可靠的基础上,有助于高效地开发出可靠性较高的软件。

3、分类——CPP所支持的继承形式:

这里写图片描述

4、实现——继承的基本例子:

单继承的定义格式如下:

class 派生类名: 继承访问控制 基类类名{    成员访问控制:    成员声明列表;};

其中,继承的方式有三种:public 表示公有继承;private 表示私有继承;protected 表示保护继承;
·Note:缺省均为private
如:

class BASE{ public: BASE();    void get_ij(); protected:     int i, j; private:     int x_temp;};class Y1:public BASE{ public:    void increment(); private:    float nmember;};

5、由继承引入的新关键字——protected:

受保护成员具有private与public的双重角色:对派生类的成员函数而言,它为public,而对类的外部而言,它为private。即:protected成员只能由本类及其后代类的成员函数访问

6、继承成员的访问控制规则:

这里写图片描述
影响继承成员(派生类从基类中继承而来的成员)访问控制方式的两个因素:

1、定义派生类时指定的继承访问控制

2、该成员在基类中所具有的成员访问控制

·在大多数情况下,使用public的继承方式;private和protected是很少使用的。

7、派生类对象的存储

派生类的对象不仅存放了在派生类中定义的非静态数据成员,而且也存放了从基类中继承下来的非静态数据成员。

8、继承与构造函数、析构函数:

1、继承时的构造函数:

基类的构造函数不被继承,派生类中需要声明自己的构造函数。

派生类的构造函数中只需要对本类中新增成员进行初始化即可。对继承来的基类成员的初始化是通过自动调用基类构造函数完成的。但是派生类的构造函数需要给基类的构造函数传递参数。(见3)

2、构造函数和析构函数的调用顺序:

构造函数的调用次序(创建派生类对象时)
– 首先调用其基类的构造函数(调用顺序按照基类被继承时的声明顺序(从左向右))。
– 然后调用本类对象成员的构造函数(调用顺序按照对象成员在类中的声明顺序)。
– 最后调用本类的构造函数。

注:构造函数的调用顺序按照基类被继承时的声明顺序,而不是子类定义派生类构造函数时通过初始化列表显式调用基类构造函数时的顺序。

析构函数的调用次序
·撤销派生类对象时析构函数的调用次序与构造函数的调用次序相反
– 首先调用本类的析构函数
– 然后调用本类对象成员的析构函数
– 最后调用其基类的析构函数

如:

//Demo.hclass C { public:    C( ); //构造函数    ~C( ); //析构函数};class BASE { public:    BASE( ); // 构造函数    ~BASE( ) // 析构函数};
#include “Demo.h” //Demo.cppC::C( ) //构造函数{ cout << "Constructing C object.\n"; }C:: ~C( ) //析构函数{ cout << "Destructing C object.\n"; }BASE::BASE( ) // 构造函数{ cout << "Constructing BASE object.\n"; }BASE:: ~BASE( ) // 析构函数{ cout << "Destructing BASE object.\n"; }
class DERIVED: public BASE { // Derived.h public:    DERIVED() // 构造函数    ~DERIVED() // 析构函数 private:    C mOBJ;};
#include “Derived.h” // Derived.cppDERIVED::DERIVED() // 构造函数{ cout << "Constructing derived object.\n"; }DERIVED:: ~DERIVED() // 析构函数{ cout << "Destructing derived object.\n"; }
#include “Derived.h” // Client.cppint main(){    DERIVED obj; // 声明一个派生类的对象    // 什么也不做,仅完成对象obj的构造与析构    return 0;}

运行结果

Constructing BASE object.Constructing C object.Constructing derived object.Destructing derived object.Destructing C object.Destructing BASE object.

3、向基类构造函数传递实参:

若基类构造函数带参数,则定义派生类构造函数时通过初始化列表显式调用基类构造函数,并向基类构造函数传递实参。
一般形式是:

派生类名(形参表) : 基类名(实参表){    派生类新成员初始化赋值语句;};

9、恢复访问控制方式:

基类中的public或protected成员,因使用protected或private继承访问控制而导致在派生类中的访问方式发生改变,可以使用“访问声明” 恢复为原来的访问控制方式。
访问声明的形式:

基类名::成员名;

使用情景:在派生类中希望大多数继承成员为protected或private,只有少数希望保持为基类原来的访问控制方式。

10、继承成员重定义:

派生类中修改继承成员函数的语义(即,修改函数体,而保持函数原型不变)。
实现基础:编译器对成员函数调用的处理
这里写图片描述

11、重载继承成员:

函数名相同,但函数首部不同(即参数列表不同;当然,函数实现一般也不同)。

12、屏蔽继承成员:

目的:
– 使得客户代码通过派生类对象不能访问继承成员。
方法:
– 使用继承访问控制protected和private(真正屏蔽)
– 在派生类中成员访问控制protected或private之后定义与继承成员函数相同的函数原型,而函数体为空(非真正屏蔽,仍可通过使用“基类名::成员名”访问)

13、继承成员重命名:

目的:
– 解决名字冲突。
– 在派生类中选择更合适的术语命名继承成员。
方法:
– 在派生类中定义新的函数,该函数调用旧函数;屏蔽旧函数。
– 在派生类中定义新的函数,该函数的函数体与旧函数相同。屏蔽旧函数。

14、类型兼容性(只适用于公有派生类):

实现基础:每个派生类对象包含一个基类部分。
因此,可以将每个派生类对象当成祖先类对象使用,也可以将派生类对象赋值给祖先类对象。
且——指向基类对象的指针也可指向公有派生类对象。但是指向公有派生类对象的指针不能指向基类对象。

More:参数传递与对象初始化也有上述兼容性。

15、继承引起的名字冲突:

如果一个子类同时继承多个父类,有可能因为那几个父类中有相同名字的函数而引起名字冲突。
注意:名字冲突不会引起编译错误,因为CPP不禁止名字冲突,但是当代码运行到这一行的时候,因为编译器无法确定该调用哪个函数,所以有可能乱调用导致出错。

解决方法:
- 显示调用冲突的函数:在名字冲突函数前面加一个类的域名。
- 在子类中重定义冲突的函数。这种情况下也还是可以通过域名访问父类的函数。

16、补充:

如果基类声明被重载了,则应在派生类中重新定义所有基类版本。

class BASE { public:    virtual void funtion();    virtual void funtion(int);    virtual void funtion(double, double);};class devired : public BASE { public:    virtual void funtion();    virtual void funtion(int);    virtual void funtion(double, double);};

如果只重新定义一个版本,则另外两个版本将被隐藏,派生类对象将无法使用它们。
注意,如果不需要修改,则新定义可以只调用基类版本。

声明:以上内容整理自
中山大学万海讲师上课内容以及《C++ Primer Plus》。

1 0
原创粉丝点击