C++基础知识(六:继承)

首先我们应该知道C++的三大特性就是封装、继承和多态。

此篇文章将详细的讲解继承的作用和使用方法。

  • 继承

一个类,继承另一个已有的类,创建的过程

父类(基类)派生出子类(派生类)的过程

继承提高了代码的复用性

【1】继承的格式

复制代码
class 类名:父类名`
`{};

【2】继承的权限

复制代码
class 类名:继承的权限 父类名`
`{};`

`如果不写继承方式,默认是私有继承
复制代码
父类中的权限      public|private|protected  public|private|protected   public|private|protected              `
`继承方式                 public                    private                   protected`
`子类中的权限      public|不能访问|protected  private|不能访问|private    protected|不能访问|protected`
`

【3】继承时类中的特殊成员函数

特殊的成员函数不会被继承

构造函数:

  • 需要在子类中显性调用父类的构造函数(初始化列表中)(透传构造)
  • 透传构造
  • 继承构造
  • 委托构造

需要在子类中显性调用父类构造函数的场合:

父类中只有有参构造 ----->子类在创建类对象时,必须手动调用父类的构造函数

复制代码
#include <iostream>`

`using` `namespace` `std;`

`class` `Father`
`{`
`public:`
    `int age;`
    `char c;`
    `Father(int a,char c):age(a),c(c){cout <<` `"Father有参"` `<< endl;}`
`};`

`class` `Child:public Father    //----->私有继承`
`{`
    `int high;`
`public:`
    `void` `show()`
    `{`
`        cout << c << endl;`
    `}`
`};`

`int` `main()`
`{`
    `//Child c1;  //error,因为父类只有有参构造,而子类中没有提供能够调用父类有参构造的构造函数,不能成功创建父类的空间`
    `//Father f1;`
`    c1.age;`
`    cout <<` `sizeof` `(Father)` `<< endl;`
`    cout <<` `sizeof` `(Child)` `<< endl;`
    `return` `0;`
`}

i)透传构造

在子类构造函数的初始化列表中,显性调用父类的构造函数

ii)继承构造

C++支持

不用在子类中再写一遍父类的构造函数

使用:using Father::Father; ----->在子类中使用父类的构造函数

直接使用继承构造的方式,不能对子类成员初始化

继承构造本质上并不是把父类中的构造函数继承给子类,编译器自动根据父类中构造函数的格式,提供出派生的构造函数(个数和参数都和父类中的构造函数一致),主要还是通过透传构造创建父类的空间

复制代码
#include <iostream>`
`using` `namespace std;`

`class` `Father`
`{`
`public:`
    `int age;`
    `char c;`
`//    Father(){cout << "Father无参" << endl;}`
    `Father(int a,char c):age(a),c(c){cout <<` `"Father有参两个参数"` `<< endl;}`
    `Father(char c):c(c){cout <<` `"Father有参一个参数的"` `<< endl;}`
    `Father(Father &other):age(other.age),c(other.c)`
    `{cout <<` `"Father拷贝"` `<< endl;}`
`};`

`class` `Child:public Father    //----->私有继承`
`{`
`private:`
    `int high;`
    `//父类中的public成员age,通过公有继承,仍然是public`
    `using Father::age;`   `//把父类中公有继承下来的age成员,在子类中改成私有权限`
`public:`
    `void` `show()`
    `{`
`        cout << c << endl;`
    `}`
    `//子类的无参构造,但是显性调用父类的有参构造还给了默认值`
    `//透传构造`
`//    Child():Father(12,'a'){cout << "Child无参构造" << endl;}`
`//    Child(int a,char c,int h):Father(a,c),high(h)`
`//    {cout << "Child有参构造" << endl;}`

    `//父类中的所有构造函数,都被继承到了子类中`
    `using Father::Father;`   `//更高效一些`
`};`

`int` `main()`
`{`
`    Child c1(10);`
`    Child c2(20,'z');`
`    Child c3 = c2;`
    `//Father f1;`
    `//c1.age;`
`    cout <<` `sizeof` `(Father)` `<< endl;`
`    cout <<` `sizeof` `(Child)` `<< endl;`
    `return` `0;`
`}`
`

iii)委托构造

一个类的情况,并不直接通过无参构造实例化对象,而是无参构造委托了有参构造,实例化对象

继承时的情况

复制代码
    Child():Child(10){cout << "Child无参构造" << endl;}   //Child c1`
`    Child(int a):Father(12,'a'),high(a)`
`    {cout << "Child有参构造一个参数" << endl;}

iv)拷贝构造

需要在初始化列表中显性调用父类的拷贝构造,传other对象到父类的拷贝构造中

复制代码
Father(Father &other):age(other.age),c(other.c){cout << "Father的拷贝构造" << endl;}`
`Child(Child &other):Father(other),high(other.high){}

【4】继承时构造和析构的时机

继承关系,可以理解为包含关系

子类在继承父类时,会把父类中的成员保留一份,再来创建子类自己的成员

父类先构造,子类后构造

子类先析构,父类后析构

复制代码
#include <iostream>`

`using` `namespace std;`
`class` `F`
`{`
    `int` `*p;`
`public:`
    `F():p(new` `int){cout <<` `"F无参构造"` `<< endl;}`
    `~F()`
    `{`
        `delete p;`
`        cout <<` `"F析构函数"` `<< endl;`
    `}`
`};`
`class` `C:public F`
`{`
    `int` `*p;`
`public:`
    `C():p(new` `int){cout <<` `"C无参构造"` `<< endl;}`
    `~C()`
    `{`
        `delete p;`
`        cout <<` `"C析构函数"` `<< endl;`
    `}`
`};`

`int` `main()`
`{`
`    C *p1 =` `new C;`
    `delete p1;`   `//空间释放时,会自动调用析构函数,无需手动调用`
`    p1 =` `nullptr;`
    `return` `0;`
`}`
`

【5】父子类中存在同名成员问题

访问时不会发生冲突,默认访问子类的

复制代码
#include <iostream>`
`using` `namespace std;`
`class` `F`
`{`
`public:`
    `int` `*p;`
    `F():p(new` `int){cout <<` `"F无参构造"` `<< endl;}`
    `~F()`
    `{`
        `delete p;`
`        cout <<` `"F析构函数"` `<< endl;`
    `}`
`};`
`class` `C:public F`
`{`
`public:`
    `int` `*p;`
    `C():p(new` `int){cout <<` `"C无参构造"` `<< endl;}`
    `~C()`
    `{`
        `delete p;`
`        cout <<` `"C析构函数"` `<< endl;`
    `}`
`};`

`int` `main()`
`{`
`    C *p1 =` `new C;`
    `*(p1->p)` `=` `90;`
`    cout <<` `*(p1->p)` `<< endl;`   `//子类成员和父类成员同名,默认优先访问子类成员`
`    cout <<` `*(p1->F::p)` `<< endl;`  `//通过域限定符访问父类的成员`
    `delete p1;`   `//空间释放时,会自动调用析构函数,无需手动调用`
`    p1 =` `nullptr;`
    `return` `0;`
`}
  • 多重继承

一个子类,继承自多个基类

【1】格式

复制代码
class 类名:继承权限 父类名,继承权限 父类名····`
`{}

【2】当多个父类中包含同名成员

多个父类中包含同名成员,通过域限定符访问指定的父类中成员

复制代码
#include <iostream>`

`using` `namespace std;`

`class` `Room`
`{`
`public:`
    `void` `clean()`
    `{`
`        cout <<` `"打扫房间"` `<< endl;`
    `}`
`};`
`class` `BedRoom`
`{`
`public:`
    `void` `play()`
    `{`
`        cout <<` `"可以玩游戏"` `<< endl;`
    `}`
    `void` `clean()`
    `{`
`        cout <<` `"打扫卧室"` `<< endl;`
    `}`
`};`

`//Home类公共继承字Room和BedRoom类`
`class` `Home:public Room,public BedRoom`
`{`
`};`

`int` `main()`
`{`
`    Home h1;`
`    h1.play();`
`    h1.Room::clean();`
`    h1.BedRoom::clean();`
    `return` `0;`
`}
  • 菱形继承(钻石继承)

【1】格式

复制代码
     A                ----->公共基类`
   `/   \`
`  B     C             ------>中间子类`
`   \   /`
`     D                ------>汇集子类

汇集子类中,会包含两份公共基类中的内容

【2】菱形继承存在的问题

  1. 会发生二义性的问题(同一个变量或者函数,可以通过两种方法访问)
  2. 如果公共基类,过大,会造成汇集子类中的代码膨胀/冗余
复制代码
#include <iostream>`

`using` `namespace std;`
`class` `A`
`{`
`public:`
    `int a;`
    `//A(int a):a(a){cout << "A" << endl;}`
`};`

`class` `B:public A`
`{`
`public:`
    `int c;`
    `//B(int a,int c):A(a),c(c){cout << "B" << endl;}`
`};`

`class` `C:public A`
`{`
`public:`
    `//C(int a):A(a){cout << "C" << endl;}`
`};`

`class` `D:public C,public B`
`{`
`public:`
    `//D(int a,int c):B(a,c),C(a),A(a){cout << "D" << endl;}`
`};`

`int` `main()`
`{`
`    D d1;`
`    d1.B::a =` `90;`   `//二义性,还可以直接通过中间子类访问,直接访问B中的a成员`
    `//cout << d1.C::A::a << endl;  //会发生二义性,因为访问A,但是有两条路径都访问到A`
    `return` `0;`
`}

【3】虚继承(virtual)

虚继承指对公共基类的虚继承。

主要用于解决菱形继承问题,

采用虚继承后,公共基类中的内容,只会在汇集子类中存在一份,在汇集子类中,可以通过任意一条路径访问到公共基类中的成员

复制代码
#include <iostream>`

`using` `namespace std;`
`class` `A`
`{`
`public:`
    `int a;`
`};`

`class` `B:virtual public A`
`{`
`public:`
    `int c;`
`};`

`class` `C:virtual public A`
`{`

`};`

`class` `D:public B,public C`
`{`

`};`

`int` `main()`
`{`
`    D d1;`
`    d1.B::A::a =` `90;`
`    cout << d1.C::A::a << endl;`
    `return` `0;`
`}`
`
相关推荐
Hello-Mr.Wang4 分钟前
vue3中开发引导页的方法
开发语言·前端·javascript
救救孩子把7 分钟前
Java基础之IO流
java·开发语言
WG_178 分钟前
C++多态
开发语言·c++·面试
宇卿.15 分钟前
Java键盘输入语句
java·开发语言
Amo Xiang25 分钟前
2024 Python3.10 系统入门+进阶(十五):文件及目录操作
开发语言·python
friklogff37 分钟前
【C#生态园】提升C#开发效率:深入了解自然语言处理库与工具
开发语言·c#·区块链
Charles Ray2 小时前
C++学习笔记 —— 内存分配 new
c++·笔记·学习
重生之我在20年代敲代码2 小时前
strncpy函数的使用和模拟实现
c语言·开发语言·c++·经验分享·笔记
爱上语文2 小时前
Springboot的三层架构
java·开发语言·spring boot·后端·spring
编程零零七4 小时前
Python数据分析工具(三):pymssql的用法
开发语言·前端·数据库·python·oracle·数据分析·pymssql