--------------------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();//虚析构函数
virtualdouble calcArea();//虚函数
};
#endif
Shape.cpp:
#include"Shape.h"
Shape::Shape()
{
cout << "Shape()" << endl;
}
Shape::~Shape()
{
cout << "~Shape()" << endl;
}
double Shape::calcArea()
{
cout << "Shape::calcArea()" << endl;
return0;
}
Rect.h:
#ifndef RECT_H
#define RECT_H
#include"Shape.h"
class Rect:public Shape
{
public:
Rect(double width, double height);
virtual ~Rect();//虚析构函数
virtualdouble 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();//虚析构函数
virtualdouble 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;
return3.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");
return0;
}
//动态多态中存在的问题:内存泄露用虚析构函数解决
//可以试着去掉virtual 看看什么情况
//
//virtual 关键字不能修饰构造函数不能修饰普通函数(即全局函数)
//
//virtual 关键字不能修饰静态成员函数(static)
//【静态成员函数属于类不属于某个对象而virtual却修饰的是对象的函数】
//
//virtual 关键字不能修饰内联函数(inline)
//【此时inline将失去作用毕竟inline只是建议】
运行一览:
【made by siwuxie095】