--------------------siwuxie095
动态多态中存在的问题,即 内存泄露
看如下实例:
定义一个形状类:Shape
再定义一个圆类:Circle,它 public 的继承了 Shape
在 Circle 中有一个特殊的数据成员,即 Coordinate 类型的指针,该
指针所代表的就是圆心坐标,在构造函数中实例化一个坐标对象,作为
圆心坐标,并使用 m_pCenter 指向该对象,在析构函数再释放掉这个
对象
这样就能保证,在实例化 Circle 后,去使用 Coordinate 对象,使用
完成之后,还能将堆中的内存释放掉,从而保证内存不泄露
但是,在多态的使用中,如果用父类指针指向子类对象,并用父类指针
去操作子类对象中相应的虚函数,这是没有问题的,但当使用 delete
销毁对象,并且是想借助父类指针销毁子类对象时,就出现问题了
这是因为:如果 delete 后面跟的是一个父类的指针,那么它只会执行
父类的析构函数;如果 delete 后面跟的是一个子类的指针,那么它既
会执行子类的析构函数,也会执行父类的析构函数
可见:delete shape1 就只执行了 Shape 的析构函数,而没有执行 Circle
的析构函数,导致在 Circle 的构造函数中申请的内存没有释放掉,于是就
造成了内存泄露
要解决这个问题,必须要引入 虚析构函数
虚析构函数
使用 virtual 修饰的析构函数,称之为 虚析构函数
从写法上就比较简单了:在 ~Shape(); 前加上 virtual 关键字即可
给其子类 Circle 的析构函数前也加上 virtual 关键字
子类 Circle 的析构函数前,可以加 virtual,也可以不加,如果不加,
系统会自动为你加上
推荐还是加上,这样,将来再有类去继承 Circle 时,也知道 Circle 的
析构函数是带着 virtual 的,那么 Circle 子类的析构函数也就应该带上
virtual,这能够正确的帮助程序员完成 Circle 子类的定义
定义完成后,就可以在 main() 函数中使用原来的方式进行操作了:
如果此时 delete 后面跟的是一个父类指针,那么父类指针指向的是哪个
子类对象,该子类对象的析构函数就能先得以执行,然后再执行父类的析
构函数,保证内存不被泄露
既然,virtual 可以修饰普通的成员函数,也可以修饰析构函数, 那么
它是不是就没有任何限制呢?显然不是
(1)virtual 不能修饰普通的函数
即 必须得是某一个类的成员函数,而不能是一个全局函数,如果用
virtual 去修饰全局函数,就会产生编译错误
(2)virtual 不能修饰静态的成员函数
静态成员函数 不属于任何一个对象,它是和 类 同生共死的,如果用
virtual 去修饰静态成员函数,也会产生编译错误
(3)virtual 不能修饰内联函数
如果用 virtual 去修饰内联函数,对于计算机来说,它会忽略掉 inline
关键字,而使 内联函数 变成一个纯粹的虚函数
(4)virtual 不能修饰构造函数
如果用 virtual 去修饰构造函数,也会出现编译错误
程序:
Shape.h:
#ifndef SHAPE_H #define SHAPE_H #include <iostream> using namespace std;
class Shape { public: Shape(); virtual ~Shape();//虚析构函数 virtual double calcArea();//虚函数 };
#endif |
Shape.cpp:
#include "Shape.h"
Shape::Shape() { cout << "Shape()" << endl; }
Shape::~Shape() { cout << "~Shape()" << endl; }
double Shape::calcArea() { cout << "Shape::calcArea()" << endl; return 0; } |
Rect.h:
#ifndef RECT_H #define RECT_H #include "Shape.h"
class Rect:public Shape { public: Rect(double width, double height); virtual ~Rect();//虚析构函数 virtual double calcArea();//虚函数 protected: double m_dWidth; double m_dHeight; };
#endif |
Rect.cpp:
#include "Rect.h"
Rect::Rect(double width, double height) { m_dWidth = width; m_dHeight = height; cout << "Rect()" << endl; }
Rect::~Rect() { cout << "~Rect()" << endl; }
double Rect::calcArea() { cout << "Rect::calcArea()" << endl; return m_dWidth*m_dHeight; } |
Circle.h:
#ifndef CIRCLE_H #define CIRCLE_H #include "Shape.h" #include "Coordinate.h"
class Circle:public Shape { public: Circle(double r, int x, int y); virtual ~Circle();//虚析构函数 virtual double calcArea();//虚函数 protected: double m_dR; //声明了Coordinate类的指针 在构造函数中去在堆中申请内存 //析构时如果不用virtual 这段内存就会泄露 Coordinate *m_pCenter; };
#endif |
Circle.cpp:
#include "Circle.h"
Circle::Circle(double r, int x, int y) { m_dR = r; m_pCenter = new Coordinate(x, y);//在堆中申请一段内存 cout << "Circle()" << endl; }
Circle::~Circle() { delete m_pCenter; m_pCenter = NULL; cout << "~Circle()" << endl; }
double Circle::calcArea() { cout << "Circle::calcArea()" << endl; return 3.14*m_dR*m_dR; } |
main.cpp:
#include <stdlib.h> #include "Rect.h" #include "Circle.h" using namespace std;
//虚析构函数带来的好处主要是内存不被泄露 //当然,如果 Circle 类中不去从堆中申请对象,即便是没有虚析构函数也是无所谓的 //还是希望大家保持这样一个好习惯:加上 virtual 关键字,使用虚析构函数 //因为我们并不知道未来继承我们的子类是否会在其构造函数中申请内存 int main(void) { Shape *shape1 = new Rect(3, 6); Shape *shape2 = new Circle(5, 1, 2);
shape1->calcArea(); shape2->calcArea();
delete shape1; shape1 = NULL; delete shape2; shape2 = NULL;
system("pause"); return 0; }
//动态多态中存在的问题:内存泄露 用虚析构函数解决 //可以试着去掉virtual 看看什么情况 // //virtual 关键字不能修饰构造函数 不能修饰普通函数(即全局函数) // //virtual 关键字不能修饰静态成员函数(static) //【静态成员函数属于类 不属于某个对象 而virtual却修饰的是对象的函数】 // //virtual 关键字不能修饰内联函数(inline) //【此时inline将失去作用 毕竟inline只是建议】 |
运行一览:
【made by siwuxie095】