IT数码 购物 网址 头条 软件 日历 阅读 图书馆
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
图片批量下载器
↓批量下载图片,美女图库↓
图片自动播放器
↓图片自动播放器↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁
 
   -> C++知识库 -> C++ 基础与深度分析 Chapter12 类的细节(类的继承) -> 正文阅读

[C++知识库]C++ 基础与深度分析 Chapter12 类的细节(类的继承)

概述

在这里插入图片描述
通过类的继承(派生)来引入 是一个 的关系
被继承的类——基类,继承的类——继承类。

#include <iostream>
using namespace std;

struct Base
{
};

struct Base2 : Base
{
};


// 这是类的声明,下面继承部分不是类的声明
struct Drive;

// Drive派生自Base基类
// struct不写public,默认也是public继承
// 但是如果是class,那么默认是private继承,所以class经常看到public继承
struct Drive : public Base2
{

};

int main()
{
    Drive d;
    // 使用基类的指针或引用可以指向派生类对象
    // Base2继承自Base,Drive继承自Base2,还是可以的
    Base& ref = d;
    Base* ptr = &d;
}

使用基类的指针或引用可以指向派生类对象
在这里插入图片描述
静态类型 V.S. 动态类型
静态类型:编译器就可以确定的类型。
动态类型:在运行期为ref或者指针赋予的实际的类型。
在这里插入图片描述

#include <iostream>
using namespace std;

struct Base
{
    void fun() {}
};

struct Base2 : public Base
{
};

struct Drive : public Base2
{
    void fun2() {}
};

struct Drive2 : public Base2
{
};

int main()
{
    Drive d;
    Base& ref = d;
    ref.fun(); // 编译器只看静态类型是否可以接收
    // ref.fun2(); // fun2是Drive的函数,是ref的动态类型,ref不能调用动态类型的函数
    Drive d2;
    Base* ptr = &d;
    ptr = &d2; // ptr对应的静态类型没有发生改变,还是Base。但是动态类型从Drive到Drive2了
}

protected 限定符:派生类可访问

#include <iostream>
using namespace std;

struct Base
{
protected: // 如果是protected,那么Derive的fun2,就可以调用了
    void fun() {}
    int x;
};


struct Derive : public Base
{
public:
    void fun2() 
    {
        fun(); // fun如果是private,无法访问
        x; // 因为proteted,所以可以访问fun和x
    }
};


int main()
{
    Derive d;
    Base b;
    // b.fun(); // fun private不行
    d.fun2();
    // b.fun2(); // b不能访问Derive的函数
}

类的派生会形成嵌套域

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
派生类中的名称定义会覆盖基类

#include <iostream>
using namespace std;

struct Base
{
    int val = 2;
};


struct Derive : public Base
{
public:
    void fun2() 
    {
        cout <<  val << endl;
    }
    // int val = 3;
};


int main()
{
    Derive d;
    Base b;
    d.fun2(); // 3 因为Derive里的val覆盖了Base的val
    // 根本原因是Derive是嵌套在Base里的

    cout << d.val << endl; // 如果把Derive的val注释掉,那么会去找Base的val。因为他们是嵌套的关系
}

使用域操作符显式访问基类成员

#include <iostream>
using namespace std;

struct Base
{
    int val = 2;
};


struct Derive : public Base
{
public:
    void fun2() 
    {
        cout <<  val << endl;
        cout <<  Base::val << endl; // 域操作符
    }
    int val = 3;
};


int main()
{
    Derive d;
    Base b;
    d.fun2(); // 3 因为Derive里的val覆盖了Base的val

}

在派生类中调用基类的构造函数

#include <iostream>
using namespace std;

struct Base
{
    Base ()
    {
        cout << "Base constructor is called" << endl;
    }
};


struct Derive : public Base
{
public:
    Derive ()
    {
        cout << "Derive constructor is called" << endl;
    }
};


int main()
{
    Derive d; // Base构造函数先调用,再调用Derive构造函数

}
#include <iostream>
using namespace std;

struct Base
{
    Base (int)
    {
        cout << "Base constructor is called" << endl;
    }
};


struct Derive : public Base
{
public:
    Derive (int a)
        : Base(a) // 通过初始化列表来去调用基类Base的构造函数
    {
        cout << "Derive constructor is called" << endl;
    }
};


int main()
{
    Derive d(100); // Base构造函数先调用,再调用Derive构造函数

}

虚函数

在这里插入图片描述
通过虚函数与引用(指针)实现动态绑定
使用关键字 virtual 引入
非静态、非构造函数可声明为虚函数
虚函数会引入vtable结构
在这里插入图片描述

#include <iostream>
using namespace std;

struct Base
{
    virtual void baseMethod() {} // 虚函数
    int baseMember;

};


struct myClassDerive : public Base
{
    virtual void deriveMethod() {} // 虚函数
    int deriveMember;
};

struct myClassDerive2 : public myClassDerive
{
    virtual void deriveMethod2() {} // 虚函数
    int deriveMember2;
};

int main()
{
    myClassDerive2 d;
    Base& b = d;
    Base* ptr = &d;
    myClassDerive2& d2 = dynamic_cast<myClassDerive2&>(b);
    myClassDerive2* ptr2 = dynamic_cast<myClassDerive2*>(ptr);
}

如果我们想把一个基类的指针转成一个派生类的指针,如果基类的指针确实指向派生类的地址,那么这个转换就是成功的,否则指向空指针。基类的引用转换成派生类的引用,如果基类引用确实绑定在派生类的引用,就是成功的,否则抛出异常。

static_cast是在编译期处理的,但是dynamic_cast是在运行期发生的,判断动态类型能不能发生转变。如果要编写高性能的程序,dynamic_cast慎用。

#include <iostream>
using namespace std;

struct Base
{
    virtual void fun() 
    {
        cout << "Base :fun() is called" << endl;
    } 

};

struct Derive : Base
{
    void fun()
    {
        cout << "Derive :fun() is called" << endl;
    }
};

int main()
{
    Derive d;
    d.fun();
    Base& b = d;
    b.fun(); // 注意,使用虚函数,那么就会调用Derive的fun,但是如果没有使用虚函数,就会使用Base里的fun
    // 没有了虚函数,就没有个vtable。所以对象的绑定都会在编译期确定,而编译期只看静态类型,b就是Base,所以只能调用Base中的fun
}

虚函数在派生类中的重写( override )

#include <iostream>
using namespace std;

struct Base
{
    virtual void fun() 
    {
        cout << "Base :fun() is called" << endl;
    } 

};

struct Derive : Base
{
    // override 重写
    void fun()
    {
        cout << "Derive :fun() is called" << endl;
    }
};

void proc(Base& b)
{
    b.fun();
}

int main()
{
    // 动态类型实现的运行期的一个多态,也叫运行期多态
    Derive d;
    proc(d); // 这个参数的动态类型是derive,所以调用Derive的fun

    Base b;
    proc(b); // 这个参数动态、静态类型都是Base,所以调用Base的fun
}

函数签名保持不变(返回类型可以是原始返回指针 / 引用类型的派生指针 / 引用类型)

#include <iostream>
using namespace std;

struct Base2 {};

struct Derive2 : Base2 {};

struct Base
{
    virtual Base2& fun() 
    {
        cout << "Base :fun() is called" << endl;
        static Base2 b;
        return b;
    } 

};

struct Derive : Base
{
    // override 重写
    Derive2& fun()
    {
        cout << "Derive :fun() is called" << endl;
        static Derive2 inst;
        return inst;
    }
};

void proc(Base& b)
{
    b.fun();
}

int main()
{
    // 动态类型实现的运行期的一个多态,也叫运行期多态
    Derive d;
    proc(d); // 这个参数的动态类型是derive,所以调用Derive的fun

    Base b;
    proc(b); // 这个参数动态、静态类型都是Base,所以调用Base的fun
}

可以通过 = 0 声明纯虚函数,相应地构造抽象基类
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
虚函数特性保持不变
在这里插入图片描述
override 关键字
在这里插入图片描述

继承与特殊成员函数

补充知识

  C++知识库 最新文章
【C++】友元、嵌套类、异常、RTTI、类型转换
通讯录的思路与实现(C语言)
C++PrimerPlus 第七章 函数-C++的编程模块(
Problem C: 算法9-9~9-12:平衡二叉树的基本
MSVC C++ UTF-8编程
C++进阶 多态原理
简单string类c++实现
我的年度总结
【C语言】以深厚地基筑伟岸高楼-基础篇(六
c语言常见错误合集
上一篇文章      下一篇文章      查看所有文章
加:2022-07-05 23:22:06  更:2022-07-05 23:24:48 
 
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁

360图书馆 购物 三丰科技 阅读网 日历 万年历 2024年11日历 -2024/11/23 17:17:35-

图片自动播放器
↓图片自动播放器↓
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
图片批量下载器
↓批量下载图片,美女图库↓
  网站联系: qq:121756557 email:121756557@qq.com  IT数码