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++系列(8):多态性与虚函数 -> 正文阅读

[C++知识库]c++系列(8):多态性与虚函数

一 概述

本文关于多态性与虚函数,继续认识多态性的含义,了解其应用与优势;了解虚函数的引入及其机制;借用抽象类与纯虚函数进一步理解多态性与其层层抽象的特点.

二 备注?

暂时仍为初步的关于逻辑的了解为主,很多具体的细节,理解与实践都需要之后继续认知并完善,这些文章之后都会不断更新,完善,

三 目录

一 概述

二 备注

三 目录

四 正文

(一) 多态性

1 理解

2 代码

3 结果与说明

(二) 虚函数

1 理解

2 代码段

3 结果与说明

(三) 虚析构函数

1 理解

2 代码

3 结果与说明

(四) 纯虚函数与抽象类

1 理解

2 代码

3 结果与说明

(五) 参考资料


四 正文

(一) 多态性

1 理解

什么是多态性,顾名思义多态性是指多种状态.

具体含义是类中的同一个方法,在被不同对象执行时有不同的效果.让人想起了函数重载.

静态联编:编译和连接时(这个也建议去了解)确认操作过程.这就得自己本身就设置相应的代码,通过指针,引用确定适用对象.

动态联编:与静态联编不同,其可以在运行阶段进行决策(比如有的调用需要根据输入类型决定,),决定调用哪一个函数,相应地则需要跟踪基类指针或者引用指向对象的类型,这将增加开销.

对比说明:动态联编可以运行时选择特定的方法,比较灵活;但缺点是开销较大.根据c++的指导原则:不要为不使用的特性付出代价(实践,空间),因而关于动态联编虚函数,根据需要决定.

静态联编比较死板,但编译链接时便决定了调用的函数,效率高,开销小.

好的功能往往意味着更大的开销,工程中注意明确实际需求(此处体现为方法是虚方法还是非虚方法),平衡好开销与功能.

2 代码

#include <iostream>

using namespace std;
#define Huilv 0.9
class Manager
{
private:
    int x,y;

public:
    Manager(int x=0,int y=0)
    {
        this->x = x;
        this->y = y;
    }
    double Money()
    {
        return x*y;
    }
};
class Employee:public Manager
{
private:
    int r;
public:
    Employee(int x,int y,int z):Manager(x,y)
    {
        r = z;
    }
    double Money()
    {
        return Huilv*r;
    }
};

int main()
{

    Manager A(10,10);
    cout<<A.Money()<<endl;
    Employee B(10,10,20);
    cout<<B.Money()<<endl;
    Employee *p;//注意指针的类型
    p = &B;
    cout<<p->Money()<<endl;
    Manager &pp=B;//注意引用的类型
    cout<<pp.Money()<<endl;
    return 0;

}

3 结果与说明

我们可以看到,最后一个输出是出乎我们逻辑上的预料的,其实这正是静态联编的结果,该非虚函数在编译时就已经根据其引用的类型决定了将使用基类中的相应函数,输出结果,而没有根据其地址指向的类型决定调用哪个函数,这时便需要我们在执行时再决定调用.

不过这里设置了一个基类引用,对应是派生类地址,这一做法之前没想过,建议去看看,看是不是只是为了说明该静态联编的效果(目前看来这一做法正是多态性的体现).


(二) 虚函数

1 理解

刚才的多态,联编中已经提到了虚函数,此处做更具体理解.

虚函数的实现原理:

编译器在编译时,给每个对象添加一个隐藏成员,隐藏成员中有一个指向函数地址数组的指针(vtpr),这个函数数组叫虚函数表(vtbl),表中存有虚函数的地址.?

基类对象包含有指针指向基类虚函数的地址表,派生类对象也有包含指向派生类中虚函数的指针,如果派生类对象重新定义了该虚函数,则虚函数表将保存新的虚函数地址.

此处借用c++ primer 中的一个图片,图片是一种虚函数机制,且非常直观.

从以上机制也可以看到(一)中说的动态联编开销大在哪里:

1 多了一个虚函数地址表,保存函数的地址.

2 多了一个存有表的地址的指针.

3 执行虚函数调用时需要多一个查表地址的过程.

2 代码段

class Manager
{
private:
    int x,y;

public:
    Manager(int x=0,int y=0)
    {
        this->x = x;
        this->y = y;
    }
    virtual double Money()//设置为虚函数
    {
        return x*y;
    }
};

3 结果与说明

?调用的正是我们想要的函数,可以看到虚函数机制的作用.


(三) 虚析构函数

1 理解

? ? ? ?虚析构函数是将基类的析构函数申明为虚函数,当用基类的指针指向派生类的对象四方式,可以根据实际所指的对象调用相应的析构函数,先释放派生类对应的空间,后会调用基类析构函数(此为编译器所规定),若不申明为虚函数,由(二)中说明就知道,由于是静态联编,将只会调用基类的析构函数,则派生内相应的内存将不被释放,造成内存泄露.

? ? ? ?构造函数没有虚函数,调用构造函数需要在实例化对象时,而根据之前的虚函数的实现机制,虚函数是根据虚函数表与表中的虚函数指针来调用函数的,若未实例化,自然没有相应的虚函数表,自然也没有虚构造函数,从虚函数的功能角度讲也没意义,因为虚函数是实现的是若基类指针指向派生类对象,能够调用合适的函数,而构造函数本来就在实例化对象时调用来初始化对象的数据成员.

2 代码

#include <iostream>

using namespace std;
#define Huilv 0.9
class Manager
{
private:
    int x,y;
    char *str;
public:
    Manager(int x=0,int y=0)
    {
        this->x = x;
        this->y = y;
    }
    virtual double Money()
    {
        return x*y;
    }
    ~Manager()
    { delete []str;
       cout<<"Destucted main success"<<endl;
    }
};
class Employee:public Manager
{
private:
    int r;
    char *str;
public:
    Employee(int x,int y,int z):Manager(x,y)
    {
        r = z;
        str=new char[100];
    }
    double Money()
    {
        return Huilv*r;
    }
    ~Employee()
    {
        delete []str;
        cout<<"destruced Employee success"<<endl;
    }
};

int main()
{
    Manager *p;
    p=new Employee(1,1,1);
    delete p;
    return 0;
}

结果与说明

 virtual ~Manager()
    { delete []str;
       cout<<"Destucted main success"<<endl;
    }
};

3 结果与说明

?可以很明显地通过对比看到虚析构函数的作用.


(四) 纯虚函数与抽象类

1 理解

从内容上没有函数体的虚函数,从形式上,定义时后面加了"=0".

抽象类是指含有纯虚函数的类。

为什么会引入纯虚函数这样一个东西,这要拿抽象类来说:

抽象类可以为外界提供一个高度抽象与统一的接口,它不可以实例化对象,不可作为参数类型,返回值,强制转换的类型。它可以定义指针或者引用,来指向派生类从而实现多态性(建议对此句通过理论与实践更进一步地理解)。

更具体的例子比如在一个类中,定义了几个函数,其中一个函数是Student(),为什么设置为虚函数呢,因为在这个层次看来,该Student只是个Student,看不到它的具体工作,可以在其派生类中具体定义派生类的功能。

2 代码

#include <iostream>
#include<stdlib.h>
using namespace std;

class Person
{
public:
    Person(string name);
    virtual void work()=0;
private:
    string m_strname;
};
Person::Person(string name)
{
    m_strname=name;
}
class Student:public Person
{
public:
    Student(string name);
    virtual void work()=0;
private:
    string m_strname;
};
Student::Student(string name):Person(name)
{
;
}
class Junior:public Student
{
public:
    Junior(string name);
    virtual void work()
   {
       cout<<"A"<<endl;
    }
private:
    string m_strname;
};

Junior::Junior(string name):Student(name)
{
    ;
}
int main()
{
    Junior A("Lerbon");
   A.work();
    return 0;
}

3 结果与说明

上面的案例中,student与person为抽象类,若用来实例化会报错.

而用Junior可以正常实例化


(五) 参考资料

1 <<c++ primer>>虚函数部分

2 csdn文章:C++系列(纯虚函数和抽像类

https://blog.csdn.net/yuanchunsi/article/details/78833899?

(关于纯虚函数与抽像类理解较深,知识很完整)

3 c语言网:https://www.dotcpp.com/course/79

(不错的学习网站,还有题库)

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

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