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++学习日记-多态 -> 正文阅读

[C++知识库]C++学习日记-多态

1. 基本概念

1.1 分类

  • 静态多态:包括函数重载和运算符重载,复用函数名等;
  • 动态多态:派生类和虚函数(什么是虚函数?);
    ->C++虚函数详解

1.2 区别

  • 静态多态的函数地址在编译阶段就已经确定了;
  • 动态多态的函数地址在运行阶段确定。

1.3 动态多态

满足条件:

  • 类之间有继承关系;
  • 子类要重写父类的虚函数;

重写:函数的返回值类型,形参列表均相同,关键在virtual;

父类的 指针或者引用 指向子类对象时,会进行如下判断:

父类中有虚函数:

  • 子类中如果说重写了父类中的虚函数,则在全局函数中,形参是父类引用,但传入参数为子类时,执行子类的成员函数;
  • 若没有重写,则调用父类中的成员函数;

父类中没有虚函数:则在上述情况下,执行父类的成员函数。

深入一下:

  • 每实例化一个类对象,都会将这个类中的虚函数地址保存到虚函数表中去,然后使用虚函数指针记录这个地址;
  • 当子类继承时,首先从父类拷贝一份虚函数表,然后使用虚函数指针指向它,如若重写了父类虚函数,则使用重写的函数地址覆盖原来的虚函数地址,在调用时调用重写的函数;若没有重写,则仍旧调用原来的函数地址。

举例说明:

1. 父类无虚函数时的重写:

class Base
{
public:
	void doSome()
{
	cout << "基类函数调用" << endl;
}
};
class Derived :public Base
{
public:
void doSome()
{
	cout << "派生类函数调用" << endl;
}
};
```查看类布局:![在这里插入图片描述](https://img-blog.csdnimg.cn/d6951872641c49db87af23a789affc6e.jpeg#pic_center)

运行截图:在这里插入图片描述

2. 父类有虚函数时的重写:

class Base 
{ 
public:
 	virtual void doSome()
    { 
 		cout << "基类函数调用" << endl;
  	} 
}; 
class Derived :public Base
{ 
public:
 	void doSome() 
    { 
 		cout << "派生类函数调用" << endl;
	} 
}; 

类布局:
在这里插入图片描述

运行截图:
在这里插入图片描述

3. 父类有虚函数时不重写:

class Base 
{ 
public: 
	virtual void doSome()
 	{
 		cout << "基类函数调用" << endl;
    }
};
class Derived :public Base { }; 

类布局:
在这里插入图片描述

运行截图:
在这里插入图片描述

总结:

归根到底,多态关键就是父类虚函数的重写与否。

在 不同子类 被 同父类的指针或者引用 使用时,执行不同的程序,表现出不同的形态,这就是多态。

多态意义在于使用一个指针(接口),可以根据不同的实例化对象执行不同的操作。
->值得注意的一点是,在多继承问题中,子类的虚函数表只有一个<-

2. 多态的使用举例

利用多态的技术写一个计算器类。

多态优点:

  • 代码组织结构清晰
  • 可读性强
  • 利益前期和后期的扩展及维护
#include <iostream>
using namespace std;
// 计算器基类
class BaseCalculator
{
public:
    int num1;
    int num2;

    virtual int result()
    {
        return 0;
    }
};
// 加法计算器
class Add: public BaseCalculator
{
public:
    int result()
    {
        return num1 + num2;
    }
};
// 减法计算器
class Sub :public BaseCalculator
{
public:
    int result()
    {
        return num1 - num2;
    }
};
// 乘法计算器
class mult :public BaseCalculator
{
public:
    int result()
    {
        return num1 * num2;
    }
};
// 简单实现加法运算
void test1()
{
    BaseCalculator* base = new Add; // 多态,即使用父类指针指向子类

    cin >> base->num1;
    cin >> base->num2;

    cout << base->num1 << " + " << base->num2 << " = " << base->result() << endl;
}
int main()
{
    test1();
    
    system("pause");
    return 0;
}

3. 纯虚函数和抽象类

在多态中,使用的基本都是子类重写的函数,父类的虚函数基本不会使用到。这时,我们可以将父类中的虚函数改写为纯虚函数。

语法: virtual 返回值类型 函数名(参数列表) = 0;

当类中有了纯虚函数,这个类也成为抽象类。

抽象类的特点:

  • 无法实例化对象;
  • 子类必须重写抽象类的纯虚函数,不然也属于抽象类;
  • 这样做的意义是在于,可以实现多态;即要求子类必须要重写父类虚函数,不然这个多态无意义。

4. 虚析构和纯虚析构

多态中,如果子类中有属性开辟到了堆区,那么父类指针在释放时无法调用子类的析构代码。
如:测试案例中,初始化Derived类base到堆区,使用delete释放,发现没有调用子类的析构函数,即没有将子类中申请的a释放。

class Base 
{
public:
    Base() 
    {
        cout << "Base的构造函数调用" << endl;
    }
    ~Base()
    {
        cout << "Base的析构函数调用" << endl;
    }
    virtual void doSome() = 0;
};
class Derived :public Base 
{
public:
    Derived(int input)
    {
        cout << "Derived的构造函数调用" << endl;
        a = new int(input);
    }
    ~Derived() 
    {
        cout << "Derived的析构函数调用" << endl;
        delete a;
        a = NULL;
    }
    void doSome() 
    {
        cout << "doSome执行,cout a = " << *a << endl;
    }
    int *a;
};

void test1()
{
    Base* base = new Derived(10);
    base->doSome();
    delete base;
}

运行截图:在这里插入图片描述

解决方法是将父类中的析构函数改写为虚析构或者纯虚析构函数。

对代码做出如下修改:

class Base 
{
public:
    Base()
    {
        cout << "Base的构造函数调用" << endl;
    }
    virtual ~Base()	// 改写为虚析构后 // 纯虚析构结果也一样
    {
        cout << "Base的析构函数调用" << endl;
    }
    virtual void doSome() = 0;
};

运行截图:
在这里插入图片描述

特点是:

  • 可以解决父类指针释放子类对象;
  • 需要具体的函数实现。

语法和虚函数和纯虚函数类似,使用方法也类似。

但要注意,如若父类使用纯虚析构函数,需要在类外定义带有父类作用域的析构函数。
原因是父类的析构函数是无论如何都会执行的,需要有具体的代码实现,而纯虚析构仅仅相当于是一个函数的声明。
这个操作,相当于是成员函数的重写。

5. 举例:组装一个电脑

这是自己动手,根据视频学习写出来的一个案例:

–>多态案例–组装电脑<–

里面有点疑问,希望各位可以给点建议。。

6. 娱乐时刻

在这里插入图片描述
在这里插入图片描述

  C++知识库 最新文章
【C++】友元、嵌套类、异常、RTTI、类型转换
通讯录的思路与实现(C语言)
C++PrimerPlus 第七章 函数-C++的编程模块(
Problem C: 算法9-9~9-12:平衡二叉树的基本
MSVC C++ UTF-8编程
C++进阶 多态原理
简单string类c++实现
我的年度总结
【C语言】以深厚地基筑伟岸高楼-基础篇(六
c语言常见错误合集
上一篇文章      下一篇文章      查看所有文章
加:2022-07-03 10:32:27  更:2022-07-03 10:35:45 
 
开发: 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 16:38:34-

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