C++学习笔记 多态

C++学习笔记 多态

  • 一、多态简介
  • 二、静态多态 与 动态多态
    • 1、静态链接/绑定(静态多态)
    • 2、动态链接/绑定(动态多态)
  • 三、虚函数
    • 1、virtual 虚函数
    • 2、扩展: 虚析构函数
  • 四、纯虚函数与抽象类
  • 五、覆盖(重写)、重载、隐藏


版权声明:
本文为博主原创文章,请读者遵循 CC 4.0 BY-SA 版权协议
转载请附上原文出处链接和本声明


一、多态简介

什么是多态?

多态按字面的意思就是多种形态 ;
C++ 多态意味着调用成员函数时,会 根据调用函数的对象的类型执行不同的函数
C++中存在 静态多态 与 动态多态 ;

C++学习笔记 多态_第1张图片

参考文献


二、静态多态 与 动态多态

当类之间存在层次结构,并且类之间是通过继承关联时,就会用到多态。

如图1.0 创建三个继承关联的类,其内部成员 都存在函数fun()

C++学习笔记 多态_第2张图片


1、静态链接/绑定(静态多态)

函数调用在程序执行前就准备好了
有时候这也被称为早绑定,因为所调用的成员函数 在程序编译期间就已经设置好了。

图1.0 ,将创建的子类B、C 对象,指向父类A 指针对象;

调用函数 fun() 被编译器设置为父类A中的版本,导致输出的结果都为 “i am A”,这就是所谓的 静态多态

记住:函数调用在程序执行前就已经被确定下来了
记住:函数调用在程序执行前就已经被确定下来了
记住:函数调用在程序执行前就已经被确定下来了
C++学习笔记 多态_第3张图片
现在编译器看的是 它的类型 ,而不是指针的内容。


2、动态链接/绑定(动态多态)

但现在,让我们对图1.0 的A类 稍作修改,在其成员函数fun() 的声明前放置 关键字 virtual ;

如下所示修改后,再次调用刚才的程序(会发现结果与上次不同):

C++学习笔记 多态_第4张图片
现在编译器看的是 指针的内容 ,而不是它的类型。
因此,由于 A和 B类的对象的地址存储在 *a 中,所以会调用各自的 fun() 函数。

正如您所看到的,每个子类都有一个函数 fun() 的独立实现。这就是多态的一般使用方式。
有了多态,您可以有多个不同的类,都带有同一个名称但具有不同实现的函数,函数的参数甚至可以是相同的。


三、虚函数

通过上面的讲解,我们已经知道了什么是:静态链接、动态链接

笼统说明一下:
静态链接:调用 继承父类 的相关成员函数
动态链接:调用 自己类 的相关成员函数

当存在多态现象时:
我们想要 在程序中任意点,可以 根据所调用的对象类型来选择调用的函数(动态链接),则可以使用虚函数

1、virtual 虚函数

注意:虚函数不能与static 共用

虚函数 是在父类中使用关键字 virtual 声明的函数
virtual 函数的数据类型 函数名 (形参..)

在子生类中重新定义父类中定义的虚函数时,会告诉编译器不要静态链接到该函数。(虚函数的主要作用就是:实现多态性,防止产生静态链接)

2、扩展: 虚析构函数

只要是类都会存在: 构造函数 与 析构函数 这两个函数(若用户为定义,则编译器会帮你定义);

析构函数 & 构造函数 参考文献

构造函数:调用后 默认 为类创建对象
析构函数:调用后 默认 为类释放对象

当程序中存在多态且利用父级指针类操作成员函数时,若用户不为父类定义 虚析构函数可能会 因为静态链接原因 造成内存泄露的问题
详情 图2.0

C++学习笔记 多态_第5张图片
可以看到 由于程序员选择释放父级指针(而不是被创建的类对象Son),且父类的析构并不是虚析构,导致程序产生了静态链接,只释放了父类未释放子类;

解决这个问题的方法有两种:
\

方法一:new谁则delet谁 ==> 直接delet S;

方法二:将父类的析构函数变为虚析构函数,这样在delet时就不会产生静态链接,而是动态链接;
(则会先释放S再释放F)


四、纯虚函数与抽象类

什么是抽象?

动物作为一个父类可以派生出海豹、狐狸、猴子等子类,但动物本身生成对象明显不合常理,对于具体的对象来说,动物这个名词是个抽象的;(再往上就是 多细胞生物 -> 碳基生物 -> 生物 越往上越抽象)
C++学习笔记 多态_第6张图片
在很多情况下,父类本身生成对象也存在是不合情理的现象。
注意:继承抽象父类后,一定要实现父类的纯虚函数(函数名、参数列表、返回值 都一样),不然改子类将也为一个抽象类;

#include
using namespace std;

//抽象
class Animal
{
public:
    Animal(){}
    virtual ~Animal(){}
    virtual void get_name() = 0;  //纯虚函数(动物详细名称)
    virtual void get_nature() = 0;//纯虚函数(属性)
};

//--------------------------------------------
//海豹科
class Phocidae : public Animal
{
private:
    string animal_name;
public:
    Phocidae(string name) : animal_name(name){}
    ~Phocidae(){}

    void get_name()//重写 详细名称
    {
        cout << animal_name << endl;
    }
      
    void get_nature()//重写 属性
    {
        cout << "身体肥胖,皮下脂肪厚,颈粗头圆,后肢和尾连在一起" << endl;
    } 
};

//犬科
class Canidae : public Animal
{
private:
    string animal_name;
public:
    Canidae(const string &n) : animal_name(n){}
    ~Canidae(){}

    void get_name()//重写 详细名称
    {
        cout << animal_name << endl;
    }
      
    void get_nature()//重写 属性
    {
        cout << "鼻端突出,颜面部长,嗅觉灵敏,听觉发达。" << endl;
    }
};

//猴科
class Monkey : public Animal
{
private:
    string animal_name;
public:
    Monkey(const string &n) : animal_name(n){}
    ~Monkey(){}

    void get_name()//重写 详细名称
    {
        cout << animal_name << endl;
    }
      
    void get_nature()//重写 属性
    {
        cout << "吻部突出,具扁平的指甲,能直立。" << endl;
    }
};

//--------------------------------------------
int main()
{
    Phocidae Elephant_seal("象海豹");
    Phocidae Leopard_Seal("豹海豹");  
    Elephant_seal.get_name();
    Leopard_Seal.get_name();
    Leopard_Seal.get_nature();

    Canidae Grey_wolf("灰狼");
    Canidae Fox("狐狸");      
    Grey_wolf.get_name();
    Fox.get_name();
    Fox.get_nature();

    Monkey  Macaque("猕猴");
    Monkey  Baboon("狒狒"); 
    Macaque.get_name();
    Baboon.get_name();
    Baboon.get_nature();  
    
	return 0;
}

输出结果:
C++学习笔记 多态_第7张图片

抽象类相关知识参考文献


五、覆盖(重写)、重载、隐藏

C++学习笔记 多态_第8张图片

C++学习笔记 多态_第9张图片

你可能感兴趣的:(C++,基础,c++,学习,开发语言)