C++(多态)

今天学习黑马视频中的类的特性之一:多态,根据学习内容进行复习,如下,参考《黑马》——C++核心编程_漏网小鱼的博客-CSDN博客,若侵权,请告知,会删。

  1. 基本语法
  2. 原理剖析
  3. 纯虚函数和抽象类
  4. 虚析构和纯虚析构

多态分类:

静态多态:函数重载和运算符重载等,复用了函数名

动态多态:派生类和虚函数在运行时多态

静态多态为函数地址早绑定,在编译阶段已经确定函数地址

多态多态为函数地址晚绑定,在运行时确定函数地址

1、基本语法

满足条件:

  1. 有继承关系
  2. 子类重写父类中的虚函数

使用方法:

  1. 父类指针或引用指向子类对象
  2. 调用的为父类中的(虚)函数,不同的接口进入

如:Cat cat; Animal &animal =cat(类实例化对象);

Animal *animal=new Cat(类);

实例化的为一个子类(可以直接类实例化如Cat cat,也可以在堆区开辟一块新的内存空间如new Cat),然后用父类指针或者引用指向该子类,然后(可以初始化父类中的成员数据,)调用父类中的(虚)函数即实现多态。相当于后续操作都是针对父类,只是前期将父类指针或引用指向子类,父类中的函数为虚函数,所以会从子类中进入。

个人理解为父类指针或引用指向子类对象,当调用父类中的(虚)函数时,会提供不同的接口,根据下一节知识可知,父类中当有虚函数时,会有一个虚函数指针vfptr,该指针会指向虚函数表vftable,而由于指针的指向为子类,实际运行函数会为子类中重写的父类虚函数,所以当父类指针或引用指向不同的子类对象时,会从不同的接口进入运行。

重写的定义:函数返回值类型,函数名,参数列表完全一致

多态优点:

  • 代码结构清晰
  • 可读性强
  • 利于前期和后期的扩展和维护

提倡多态程序架构,好处多多~

2、原理剖析

当父类中只有函数时,为1个字节,当该函数变为虚函数时,为4个字节,因为存储变成了一个指针。

参考:黑马程序员匠心之作|C++教程从0到1入门编程,学习编程不再难_哔哩哔哩_bilibili

Animal中有一个4个字节的指针vfptr,该指针指向虚函数表, 虚函数表记录的为虚函数函数入口的地址,当子类重写父类的虚函数时,子类中的虚函数表会替换成子类的虚函数地址,当父类的指针或者引用指向子类对象时,本身还是子类对象,如创建的是cat对象,当调用公共接口speak时,会从子类中找函数的实际入口地址即Cat::speak。

开发命令窗口验证:

Animal 类

 Cat类speak函数没有重写时,直接继承Animal类:speak函数继承Animal::speak

 Cat类speak函数重写时:speak变成Cat::speak

 3、纯虚函数和抽象类

多态中父类的虚函数的实现都是无意义的,因为调用的都是子类中的重写的内容,因此将虚函数改为纯虚函数。

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

如:virtual void func() = 0;

当类中有纯虚函数时,该类也称为抽象类。

抽象类特点:

  • 无法实例化对象
  • 子类必须重写抽象类中的纯虚函数,不然子类也属于抽象类

 4、虚析构和纯虚析构

当子类中有属性开辟到堆区时,父类指针在释放时无法调用子类的析构函数,造成内存泄漏,将父类中的析构函数改为虚析构或者纯虚析构可以解决该问题。若子类中无属性开辟到堆区,则不需要父类中的析构函数改为虚析构和纯虚析构。

虚析构和纯虚析构共性:

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

虚析构和纯虚析构个性:

  • 纯虚析构所在的类为抽象类,无法实例化对象

虚析构:virtual ~类名(){}

纯虚析构:virtual ~类名() = 0; 

                  类名::~类名(){}

(纯虚析构函数相当于类内声明,类外实现)

释放子类堆区对象:

~Cat()
{
    cout<<"Cat析构函数调用"<<endl;
    if (this->m_Name!=NULL)
    {
        delete m_Name;
        m_Name=NULL;
    }
}

C++中的多态(Polymorphism)是指在父类和子类之间的相互转换,以及在不同对象之间的相互转换。 C++中的多态性有两种:静态多态和动态多态。 1. 静态多态 静态多态是指在编译时就已经确定了函数的调用,也称为编译时多态C++中实现静态多态的方式主要有函数重载和运算符重载。 函数重载是指在同一作用域内定义多个同名函数,但它们的参数列表不同。编译器根据传递给函数的参数类型和数量来确定调用哪个函数。例如: ```c++ void print(int num) { std::cout << "This is an integer: " << num << std::endl; } void print(double num) { std::cout << "This is a double: " << num << std::endl; } int main() { int a = 10; double b = 3.14; print(a); // 调用第一个print函数 print(b); // 调用第二个print函数 } ``` 运算符重载是指对C++中的运算符进行重新定义,使其能够用于自定义的数据类型。例如: ```c++ class Complex { public: Complex(double real, double imag) : m_real(real), m_imag(imag) {} Complex operator+(const Complex& other) const { return Complex(m_real + other.m_real, m_imag + other.m_imag); } private: double m_real; double m_imag; }; int main() { Complex a(1.0, 2.0); Complex b(3.0, 4.0); Complex c = a + b; // 调用Complex类中重载的+运算符 } ``` 2. 动态多态 动态多态是指在运行时根据对象的实际类型来确定调用哪个函数,也称为运行时多态C++中实现动态多态的方式主要有虚函数和纯虚函数。 虚函数是在父类中定义的可以被子类重写的函数,使用virtual关键字声明。当一个对象的指针或引用指向一个子类对象时,调用虚函数时会根据实际的对象类型来确定调用哪个函数。例如: ```c++ class Shape { public: virtual void draw() { std::cout << "Drawing a shape." << std::endl; } }; class Circle : public Shape { public: void draw() override { std::cout << "Drawing a circle." << std::endl; } }; int main() { Shape* shape_ptr = new Circle(); shape_ptr->draw(); // 调用Circle类中重写的draw函数 } ``` 纯虚函数是在父类中定义的没有实现的虚函数,使用纯虚函数声明(如virtual void func() = 0;)。父类中包含纯虚函数的类称为抽象类,抽象类不能被实例化,只能作为基类来派生子类子类必须实现父类的纯虚函数才能实例化。例如: ```c++ class Shape { public: virtual void draw() = 0; }; class Circle : public Shape { public: void draw() override { std::cout << "Drawing a circle." << std::endl; } }; int main() { Shape* shape_ptr = new Circle(); shape_ptr->draw(); // 调用Circle类中重写的draw函数 } ```
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值