一、继承的相关基本概念

1、继承的定义

    在C++中,可以使用继承来使新类得到已定义的一些类中的特性,这就好比与孩子从父亲母亲得到遗传类似,所以我们称原有的类为基类或父类,用原有类来生成新的类的过程称为派生,所以生成的新类称之为派生类或者子类。

2、 继承的声明

     在继承中和上面所说的遗传是有区别的,孩子只能遗传其父母的一些基因,而在C++的继承中,一个新的类是可以继承多个不同的类,被称为多重继承。所以继承分为单继承和多继承。
     继承的定义格式如下:
   在一个类中,我们知道其成员的访问类型分为三种,公有(public),保护(protected)和私有(private)。相应的继承方式也分为这三种,他们的区别也就在于派生类的成员和类外对象在访问它从基类继承来的的成员的访问权限不同。
   关于三种继承方式后的成员访问权限变换关系如下表所示:
      
其实这个表我自己是看着很烦,其实很容易记的,根本就不需要看这个表的。你只要记得基类的private成员在派生类中始终是不可访问的,然后记着private>protected>public,高访问权限的继承方式会改变低的访问权限成员的访问权限。说起来还是比较绕口,其实并不需要记,你看一遍肯定会牢记在心的,不信你试试看。
可以看出来,不管是哪一种继承方式,在派生类内都可以访问基类的非私有成员,基类的私有成员虽然被继承了,但是并不是可见的。而对于保护和私有继承方式,类外对象并不能访问基类的成员,公有继承方式下可以访问基类的公有成员。

二、构造函数和析构函数

在派生类中,从基类继承而来的成员初始化需要调用基类的构造函数,派生类中新增的成员初始化则调用自己的构造函数。   

1.继承中的构造函数

这里,当基类没有定义自己的构造函数,则在派生类中也可以不用定义,在构造对象时自行调用默认构造函数,但一般在编译器里会认为构造函数并不做什么事情所以对代码进行优化,并不生成默认构造函数,当你在转到反汇编看汇编代码的时候是看不到调用构造函数的语句的。
当基类显式地定义了自己的构造函数,则编译器为派生类生成默认构造函数,而且当基类没有缺省的构造函数的时候,就必须为派生类定义构造函数且在初始化列表中显式的给出基类名和其参数列表,不然在派生过程中编译器就不知道如何调用基类构造函数。
上面这两点其实与在类中包含另一个类的对象的情况差不多,我们也很容易理解。那么派生类到底能不能继承基类的构造函数呢?
这一点很多人说法都不一样,有人说派生类继承了基类的构造函数,因为你在构造一个派生类对象的时候会调用基类的构造函数。其实准确的说应该是带引号的继承,之所以能够调用基类的构造函数,是因为编译器使基类的构造函数在派生类中可见,在创建派生类的对象时会先调用基类的构造函数。  
构造函数的调用顺序:
因为在创建派生类对象的时候,要先给它继承基类的成员,所以当程序走到派生类的构造函数的时候会先调用基类的构造函数,所以对于构造函数的调用顺序,是按照继承列表的顺序依次调用每一个基类的构造函数,然后调用派生类自己的构造函数,再执行它的函数体。当然如果派生类中还有别的类对象,则先调用此对象类的构造函数再调用派生类自己的构造函数。

2.继承中的析构函数

析构函数是不能被继承的,同时一般会把基类的析构函数定义成虚析构函数:
 1 class Base
 2 {
 3 public:
 4     Base(){}
 5     virtual ~Base()
 6     {
 7         cout << "~Base()" << endl;
 8     }
 9 public:
10     int _pub1;
11 };
12 class Derived:public Base
13 {
14 public:
15     Derived(int k=1)
16     {
17         buf = new char[k];
18     }
19     ~Derived()
20     {
21         delete[] buf;
22         cout << "~Derived()" << endl;
23     }
24 public:
25     char* buf;
26 };
27
28 void test()
29 {
30
31     Base* b = new Derived(5);
32     delete b;
33 }
34 int main()
35 {
36     test();
37     return 0;
38 } 

如果不定义成虚函数,在delete时只会调用基类的析构函数而不会调用派生类的析构函数而导致内存泄漏。虚函数在这里不细讲了。
析构函数的调用顺序:
析构函数的调用情况,我们一般认为和压栈类似,所以析构函数的调用顺序如下:

三、继承中的同名隐藏

在C++中我们知道有重载,当在同一作用域内函数名相同且函数的参数列表不同,就会构成重载,这样就可以根据传参的不同来调用相应的函数,而不会存在二义性。
但是在派生类中如果有一个和基类中同名的函数,那么在派生类中基类的这个函数就是被屏蔽的,当你用派生类对象调用这个函数一定是调用派生类中的函数,即使两个函数的参数列表不同。但其实基类的这个函数还是被继承了的,要想调用可以使用作用域解析符进行调用。(对于一个同名的成员也是同样的道理)。例子如下:
 1 class A
 2 {
 3 public:
 4     void test(int)
 5     {
 6         cout << "test1()" << endl;
 7     }
 8
 9 public:
10     int _pub1;
11 protected:
12     int _pro1;
13 private:
14     int _pri1;
15 };
16
17 class B :public A
18 {
19 public:
20     void test()
21     {
22         cout << "test2()" << endl;
23     }
24 public:
25     int _pub2;
26 protected:
27     int _pro2;
28 private:
29     int _pri2;
30 };
31 int main()
32 {
33     B b;
34     b.test();         //编译错误
35     b.test(3);      //编译错误
36     b.A::test(3);   //正确
37     return 0;
38 }

四、继承中的赋值兼容规则
在此之前,我们看看基类和派生类的对象模型:
 1 class Base
 2 {
 3 public:
 4     Base() { cout << "Base()" << endl; }
 5     ~Base() { cout << "~Base()" << endl; }
 6 public:
 7     int _pub1;
 8 protected:
 9     int _pro1;
10
11 };
12 class Derived :public Base
13 {
14 public:
15     Derived()   { cout << "Derived()" << endl; }
16     ~Derived()  { cout << "~Derived()" << endl; }
17 public:
18     int _pub2;
19 protected:
20      int _pro2;
21 };
22 int main()
23 {
24     return 0;
25 }

假如就这样定义基类和派生类,那么派生类继承了基类的_pub1和pro1成员
赋值兼容规则如下:
1、派生类对象可以直接赋值给基类对象
基类对象不能赋值给派生类对象
2、基类类型指针可以指向派生类对象(派生类对象可以初始化基类的引用)
派生类类型指针不可以指向基类对象
这里也比较容易理解(当然是在public继承下),一个派生类对象本来就是从它的基类继承而来的,向上图中的对象模型,在派生类中有与基类对应的一个模块是继承来的成员,那么在赋值过程中编译器是可以把相应的基类部分赋值给基类对象。而对于把一个基类对象赋值给派生类对象的话,

五、理解“is a”和“has a”

我一开始就很不理解为啥要总结这么样的关系,还这么抽象,什么东西啊。后来写写代码也确实慢慢领会了一点,还是有那吗一丝丝韵味在其中的。
is a:
is a就是有一个,对于public继承,就有着is a特性。在上面的赋值兼容规则中也说到了,一个派生类对象时可以赋值给基类对象的,所以派生类可以代替任何需要直接基类的地方。is a就是代表了这种继承关系。对于多继承或者在派生类对象中有新增加的东西,这种关系相当于is like。
has a:
has a一般用来描述是组合这种关系的,就是在某一个类中有另一个类。那么在这个类中就可以用它包含的类的成员及成员函数,有时候我们可以把保护和私有继承也看成是一种has a关系,因为只有在类内才可以访问基类的成员。
is a相当于父亲在儿子家干活,而has a相当于雇别人在家里干活。大概就是这么个意思,大家能理解就行了,对于组合和继承这里就不展开讲了,它们各有优缺点和用武之地。在我们利用继承的时候,并不是说我们需要在这个类中使用另一个类的某些东西就继承它,要保证这个类是和基类是有is a关系的,比如说老虎是动物的一种,这才叫继承。

 

转载于:https://www.cnblogs.com/MrListening/p/5597921.html

C++的三大特性之一继承相关推荐

  1. 面向对象的三大特性之继承

    # 面向对象的三大特性之继承# 当类之间有显著不同,并且较小的类是较大的类所需要的组件时,用组合比较好 (如机器人) # 当类之间有很多相同的功能,提取这些共同的功能做成基类,用继承比较好 (动物-- ...

  2. java三大特性 继承_java基础(二)-----java的三大特性之继承

    在<Think in java>中有这样一句话:复用代码是Java众多引人注目的功能之一.但要想成为极具革命性的语言,仅仅能够复制代码并对加以改变是不够的,它还必须能够做更多的事情.在这句 ...

  3. 【转】java提高篇(二)-----理解java的三大特性之继承

    [转]java提高篇(二)-----理解java的三大特性之继承 原文地址:http://www.cnblogs.com/chenssy/p/3354884.html 在<Think in ja ...

  4. Java提高篇 —— Java三大特性之继承

    一.前言 在<Think in java>中有这样一句话:复用代码是Java众多引人注目的功能之一.但要想成为极具革命性的语言,仅仅能够复制代码并对加以改变是不够的,它还必须能够做更多的事 ...

  5. Python - 面向对象编程 - 三大特性之继承

    继承 继承也是面向对象编程三大特性之一 继承是类与类的一种关系 定义一个新的 class 时,可以从某个现有的 class 继承 新的 class 类就叫子类(Subclass) 被继承的类一般称为父 ...

  6. 【Golang第8章:面向对象编程】Go语言的结构体是什么,怎么声明;Golang方法的调用和声明;go语言面向对象实例,go语言工厂模式;golang面向对象的三大特性:继承、封装、多态

    介绍 这个是在B站上看边看视频边做的笔记,这一章是Glang面向对象编程 这一章内容较多,内容有Go语言的结构体是什么,怎么声明:Golang方法的调用和声明:go语言面向对象实例,go语言工厂模式: ...

  7. 面向对象之:三大特性:继承(已讲),封装,多态

    目录 1.封装 2.多态 3.类的约束 4. super()深入了解 前言: python面向对象的三大特性:继承,封装,多态. 1. 封装: 把很多数据封装到⼀个对象中. 把固定功能的代码封装到⼀个 ...

  8. 04 面向对象之:三大特性:继承,封装,多态

    前言: Python面向对象的三大特性:继承,封装,多态 1.封装:把很多数据封装到一个对象中,把固定功能的代码封装到一个代码块,函数,对象,打包成模块.这都属于封装的思想.具体的情况具体分析,比如, ...

  9. Python面向对象之:三大特性:继承,封装,多态以及类的约束

    前言: python面向对象的三大特性:继承,封装,多态. 1. 封装: 把很多数据封装到⼀个对象中. 把固定功能的代码封装到⼀个代码块, 函数, 对象, 打包成模块. 这都属于封装的思想. 具体的情 ...

  10. 04 面向对象之:三大特性:继承(已讲),封装,多态。

    前言: python面向对象的三大特性:继承,封装,多态. 1. 封装: 把很多数据封装到⼀个对象中. 把固定功能的代码封装到⼀个代码块, 函数, 对象, 打包成模块. 这都属于封装的思想. 具体的情 ...

最新文章

  1. 从咨询公司学到的思考分析方法
  2. 索引名 oracle,ORACLE 索引名称矫情
  3. 【Java基础】容器
  4. 使用StateServer机制来解决session丢失而造成用户验证失败
  5. 以云原生的名义,举办一次技术聚会
  6. Aix oracle 自动启动,AIX如何自动启动和关闭软件的运行
  7. Linux--网卡聚合简单脚本(bond0)
  8. 二叉树的建立和遍历算法 - 数据结构和算法47
  9. FPGA的ip核之概念和分类
  10. Linux:I/O多路转接之select(有图有代码有真相!!!)
  11. python调试神器_Python里三个最高逼格的调试神器
  12. Asp.Net NPOI excl文件导入导出
  13. composer 设置代理
  14. skyline 环境配置
  15. 消息队列MQ的基础知识
  16. Lightbox JS 用法
  17. AST混淆实战|仿obfuscator混淆控制流平坦化(超详细版)
  18. 免费的移动硬盘数据恢复软件EasyRcovery15
  19. Go--Redis快速入门指南
  20. 低通滤波器转带通滤波器公式由来_开关电源电磁兼容进级EMI传导输入滤波器的设计理论(EDTEST上海)...

热门文章

  1. echart多个柱状图 设置y轴显示_Echart可视化学习笔记(五)
  2. 【Tensorflow】深度学习实战04——Tensorflow实现VGGNet
  3. Leetcode —— 208. 实现 Trie (前缀树)(Python)
  4. Pandas数据操作
  5. 深度学习的实用层面 —— 1.3 机器学习基础
  6. leetcode - 712. 两个字符串的最小ASCII删除和
  7. 【例题+习题】【数值计算方法复习】【湘潭大学】(二)
  8. PyTorch 搭建神经网络 (MNIST)[含注释]
  9. Nginx担当WebSockets代理
  10. GOF之结构型模式Ⅰ(重点)