C++学习笔记 多态
版权声明:
本文为博主原创文章,请读者遵循 CC 4.0 BY-SA 版权协议
转载请附上原文出处链接和本声明
一、多态简介
什么是多态?
多态按字面的意思就是多种形态 ;
C++ 多态意味着调用成员函数时,会 根据调用函数的对象的类型 来 执行不同的函数。
C++中存在 静态多态 与 动态多态 ;
二、静态多态 与 动态多态
当类之间存在层次结构,并且类之间是通过继承关联时,就会用到多态。
如图1.0 创建三个继承关联的类,其内部成员 都存在函数fun()
1、静态链接/绑定(静态多态)
函数调用在程序执行前就准备好了
有时候这也被称为早绑定,因为所调用的成员函数 在程序编译期间就已经设置好了。
接 图1.0 ,将创建的子类B、C 对象,指向父类A 指针对象;
调用函数 fun() 被编译器设置为父类A中的版本,导致输出的结果都为 “i am A”,这就是所谓的 静态多态;
记住:函数调用在程序执行前就已经被确定下来了
记住:函数调用在程序执行前就已经被确定下来了
记住:函数调用在程序执行前就已经被确定下来了
现在编译器看的是 它的类型 ,而不是指针的内容。
2、动态链接/绑定(动态多态)
但现在,让我们对图1.0 的A类 稍作修改,在其成员函数fun() 的声明前放置 关键字 virtual ;
如下所示修改后,再次调用刚才的程序(会发现结果与上次不同):
现在编译器看的是 指针的内容 ,而不是它的类型。
因此,由于 A和 B类的对象的地址存储在 *a 中,所以会调用各自的 fun() 函数。
正如您所看到的,每个子类都有一个函数 fun() 的独立实现。这就是多态的一般使用方式。
有了多态,您可以有多个不同的类,都带有同一个名称但具有不同实现的函数,函数的参数甚至可以是相同的。
三、虚函数
通过上面的讲解,我们已经知道了什么是:静态链接、动态链接
笼统说明一下:
静态链接:调用 继承父类 的相关成员函数
动态链接:调用 自己类 的相关成员函数
当存在多态现象时:
我们想要 在程序中任意点,可以 根据所调用的对象类型来选择调用的函数(动态链接),则可以使用虚函数;
1、virtual 虚函数
注意:虚函数不能与static 共用
虚函数 是在父类中使用关键字 virtual 声明的函数。
virtual 函数的数据类型 函数名 (形参..)
在子生类中重新定义父类中定义的虚函数时,会告诉编译器不要静态链接到该函数。(虚函数的主要作用就是:实现多态性,防止产生静态链接)
2、扩展: 虚析构函数
只要是类都会存在: 构造函数 与 析构函数 这两个函数(若用户为定义,则编译器会帮你定义);
构造函数
:调用后 默认 为类创建对象
析构函数
:调用后 默认 为类释放对象
当程序中存在多态且利用父级指针类操作成员函数时,若用户不为父类定义 虚析构函数可能会 因为静态链接原因 造成内存泄露的问题;
详情 图2.0
可以看到 由于程序员选择释放父级指针(而不是被创建的类对象Son),且父类的析构并不是虚析构,导致程序产生了静态链接,只释放了父类未释放子类;
解决这个问题的方法有两种:
\方法一:new谁则delet谁 ==> 直接delet S;
方法二:将父类的析构函数变为虚析构函数,这样在delet时就不会产生静态链接,而是动态链接;
(则会先释放S再释放F)
四、纯虚函数与抽象类
动物作为一个父类可以派生出海豹、狐狸、猴子等子类,但动物本身生成对象明显不合常理,对于具体的对象来说,动物这个名词是个抽象的;(再往上就是 多细胞生物 -> 碳基生物 -> 生物 越往上越抽象)
在很多情况下,父类本身生成对象也存在是不合情理的现象。
注意:继承抽象父类后,一定要实现父类的纯虚函数(函数名、参数列表、返回值 都一样),不然改子类将也为一个抽象类;
#include<iostream>
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;
}
输出结果: