Cpp_继承

来源:互联网 发布:ubuntu是什么 编辑:程序博客网 时间:2024/05/21 22:40

一、继承的基本概念

第一级抽象:从具体的对象抽象出共同的属性和行为。

学生:姓名、年龄、学号、吃饭、睡觉、学习教师:姓名、年龄、工资、吃饭、睡觉、授课

第二级抽象:将类型之间共性抽取出来形成超集,将类型之间的个性具体化的不同子集中。

人类:姓名、年龄、吃饭、睡觉 - 超集体现共性 - 基类 | 派生 ^
学生是人类的一部分:学号、学习 \ 子集 - 子类 V | 继承
教师是人类的一部分:工资、授课 / 体现个性

二、继承的语法形式

class Human {public:    void eat (string const& food) { ... }    void sleep (int durations) { ... }private:    string m_name;    int m_age;};class Student : public Human {public:    void learn (string const& course) { ... }private:    int m_no;};class Teacher : public Human {public:    void teach (string const& course) { ... }private:    double m_salary;};Student s (...);s.eat ("KFC");s.sleep (1);s.learn ("C++");Teacher t (...);t.eat ("狗不理");t.sleep (10);t.teach ("UC");class 子类 : 继承方式1 基类1, 继承方式2 基类2, ... { ... };                ^^^^^^^^^^^^^^^^^^^^^^^^                                        继承表

三、公有(继承方式为public)继承的基本特点

1.皆然性

1)逻辑结构:任何时候子类对象都可以被看做是基类对象。

   学生—是—人   教师—是—人   子类和基类的关系:ISA,是一个。

从逻辑上看,基类的范畴不小于子类。

2)物理结构:任何时候基类的子类对象一定都是其基类子对象的父对象。

从物理上看,子类对象的范围不小于基类对象。

2.向上和向下造型

1)向上造型:将子类类型的指针或引用转换基类类型指针或引用。这种操作性缩小的类型转换,在编译器看来是安全的。因此可以隐式完成。
2)向下造型:将基类类型的指针或引用转换子类类型指针或引用。这种操作性放大的类型转换,在编译器看来是危险的。因此必须显式完成。
3)编译对类型安全所做的检测,仅仅基于指针或引用本身的数据类型,而与其实际目标对象无关。基类指针或引用实际目标对象的类型,究竟是否与需要转换的指针或引用的目的类型一致,完全由程序员自己判断。如果一致,则可以通过静态类型转换static_cast显式地完成向下造型。

3.子类继承基类的成员

1)在子类中或通过子类,可以直接访问基类中的所有公有和保护成员,就如同它们是在子类中声明的一样。

2)基类的私有成员在子类虽然存在但是却不可见,故无法直接访问,但是通过基类提供的公有或者保护接口可以间接访问。

3)基类的构造函数和析构函数,子类是无法继承的,但是可以在子类自己的构造函数中显式指明其基类部分的初始化方式。

4.子类隐藏基类的成员

1)尽管基类的公有和保护成员在子类中直接可见,但仍然可以在子类中重新定义这些名字,子类中的名字会隐藏基类中同名定义。

2)如果需要在子类中或通过子类访问一个在基类中定义却被子类所隐藏的名字,可以借助作用域限定操作符“::”显式指明。

四、继承方式与访问控制

1.C++为类的成员提供了三种访问控制限定符:public、protected和private,影响访问该类成员的主体和位置。

访问控制 访问控制 内部 子类 外部 友元
限 定 符 属 性 访问 访问 访问 访问
public 公有成员 OK OK OK OK
protected 保护成员 OK OK NO OK
private 私有成员 OK NO NO OK

2.C++为子类提供了三种从基类继承的方式:public、protected和private,影响通过子类访问基类中的成员的可访问性。

基类中的 在公有子 在保护子 在私有子
类中变成 类中变成 类中变成
公有成员 公有成员 保护成员 私有成员
保护成员 保护成员 保护成员 私有成员
私有成员 私有成员 私有成员 私有成员

3.私有继承和保护继承

class DCT {public:    void codec (void) { ... }}; // $100class Jpeg : protected DCT {public:    void render (void) {        ...        codec ();        ...    }};class Jpeg2000 : public Jpeg {public:    void render (void) {        ...        codec (); // Ok        ...    }};------------------------------Jpeg jpeg (...); // RMB1.00jpeg.render ();...jpeg.codec (); // No...

私有继承,又称实现继承,防止基类中的公有接口从子类中扩散出去。而保护继承仅仅防止基类中的公有接口向继承链外部扩散,但是对继承链内部的子类不做限制。

五、子类的构造、析构和拷贝

1.子类的构造函数

1)如果子类的构造函数没有显式指明其基类子对象的初始化方式,那么编译器一定会调用基类的缺省构造函数初始化该子对象。

2)如果希望子类对象的基类部分以有参的方式被初始化,就需要在子类构造函数的初始化表(而非函数体)中显式指明其基类子对象的初始化方式

2.子类的析构函数

1)子类的析构函数,无论是自己定义的,还是系统缺省提供的,在执行完其中的析构代码,并析构完所有类类型成员子对象以后,一定会自动调用其基类的析构函数,析构该子类对象中的基类子对象。
构造:分配内存->调用子类的构造函数
V
调用基类的构造函数
(按继承表的顺序)
V
调用的成员子对象类型的构造函数
(按声明的顺序)
V
执行子类的构造代码
析构: 执行子类的析构代码
V
调用成员子对象类型的析构函数
(按声明的逆序)
V
调用基类的析构函数 -> 释放内存
(按继承的逆序)
2)基类的析构函数永远不可能调用子类的析构函数。对一个指向子类对象的基类指针使用delete运算符,实际被调用的基类析构函数,该函数不会调用子类的析构函数,所释放的仅仅是基类子对象中动态分配的资源,子类特有的动态资源将形成内存泄漏。

class A { ... };class B : public A { ... };A* pa = new B (...); // pa: 指向子类对象的基类指针delete pa; // 调用A的析构函数

3.子类的拷贝构造

1)子类没有定义拷贝构造函数
系统为子类提供缺省拷贝构造函数,该函数会自动调用基类拷贝构造函数,初始化子类对象中基类子对象。
2)子类定义了拷贝构造函数,但是没有显式指明对基类拷贝构造
子类对象中的基类子对象将按照缺省方式初始化。
3)子类定义了拷贝构造函数,同时显式指明对基类也做拷贝构造
子类对象中的基类子对象将以拷贝方式做初始化。

4.子类的拷贝赋值

1)子类没有定义拷贝赋值运算符函数
系统提供的缺省拷贝赋值运算符函数自动调用基类的拷贝赋值运算符函数,复制基类子对象。
2)子类定义了拷贝赋值运算符函数,但是没有复制基类部分
基类子对象保持赋值之前的状态。
3)子类定义了拷贝赋值运算符函数,同时也复制了基类部分
基类子对象也会得到复制。

六、多重继承

1.一个子类可以同时继承自多个基类,这样的继承方式谓之多重继承。

    学生    教师       \  /      科代表        技术人员     经理     销售人员                \   /      \   /            技术主管    销售主管    电话  播放器  计算机      \     |      /        智能手机

2.名字冲突问题

一个子类的多个基类中如果存在相同的名字,当通过子类访问这些名字时,编译器会报告歧义错误——名字冲突。解决名字冲突的一般做法就是显式地通过作用域限定符指明所访问的名字继承自哪个基类。如果产生冲突的名字是成员函数且满足重载的条件,则可以通过using声明,在子类中形成重载关系,通过重载解析解决冲突。

3.钻石继承问题

1)一个子类的多个基类源自共同的基类祖先,这样的继承结构被称为钻石继承。

       人类       /  \    学生    教师       \  /      科代表                  员工               _/   |  \_            /       |      \        技术人员     经理     销售人员                \   /      \   /           技术主管    销售主管

2)派生多个中间子类(B和C)的公共基类(A)子对象,在继承自多个中间子类的汇聚子类(D)对象中,存在多个实例。在汇聚子类中,或通过汇聚子类对象,访问公共基类的成员,会因继承路径的不同而导致不一致。

3)通过虚继承可以令公共基类子对象在汇聚子类对象中的实例唯一,并为所有中间子类对象所共享。这样即使沿着不同的继承路径,所访问到的公共基类成员一定是一致的。

                  图形(位置/绘制)                     /     \        矩形(长和宽/绘制)      圆形(半径/绘制)
0 0
原创粉丝点击