都说C++内存管理是个大坑。实际上也确实是这样。
C++有析构函数,每当一个对象过期的时候,C++会执行两个动作
1.执行析构函数。
2.将对象和对象的所有数据删除。
很多人就会问了,既然有把对象删除的操作,要析构函数何用?我一开始也有一样的疑问,但是!我们都知道C++有一种神奇的类型,指针!指针他就是一个4字节的变量,甚至可以转化成int等类型打印。明白这个道理,就知道C++析构函数的作用了。
delete关键字,他后边可以接一个指针,也可以接一个例如 delete []array 这样的数组变量,其实意义都一样,它寻找的都是一个或者一组地址,把它指向的堆内存释放掉。
看一组类定义:
#pragma once#includestruct BattleValue{public: int atk; int def; std::string name; std::string desc; BattleValue(); ~BattleValue();};class Role{ public: Role(); Role(int atk, std::string name); ~Role(); Role(const Role& object); friend void show(Role role); BattleValue* data;};
这个例子中,我们看到Role类有一个变量,名字叫做data,类型是BattleValue类型的指针。其实我们在使用这个Role类的对象的时候,可以选择是否给这个data赋值一个有效值。但是有一点千万要注意,即便像构造函数中的那样给data赋值了,也不过给他赋值了一个4字节的指针变量,所以看一下源文件。
#include#include"Role.h"using std::cout;using std::endl;BattleValue::BattleValue(){ cout<<"Battle init"< atk = atk; data->name = name;}Role::Role(const Role& object){ cout<<"copy"< atk = object.data->atk; data->name = object.data->name;}Role::~Role(){ cout<<"free self"< name<<" atk:"< atk<
这段代码执行后是这样的结果
parameter roleBattle initcopyBattle initname:RockDeria atk:120free selfBattle free请按任意键继续. . .free selfBattle free
因为我是在windows下调试的,所以上述的结果的最后两行在cmd是几乎看不见的,一闪而过窗口就关闭了。这不影响我们来分析。
在程序开始的时候,利用参数的构造方法创建了role对象,然后在Role的构造方法中创建了一个BattleValue对象,然后我们取这个对象的地址赋值给了data变量。我们接下来调用Role类的友元函数,因为是传值参数调用,所以调用show方法的时候对role进行了复制。
复制的时候有调用了Role类的拷贝构造函数。我们在拷贝构造函数Role(const Role& object);中可以看到我们重新给data赋值,一个新的内存地址。
读到这里,想必大家都已经了解的差不多了。在对象过期的时候,是在调用析构函数之后把对象的所有数据都删除,但是*data是什么?不知道,C++只知道data,不会去解引用,只负责把这个4字节删除了。所以说,BattleValue对象的堆呢?GC何在?没有,不好意思内存就此泄漏。
所以我们才在Role的析构函数中追加了一个delete data 这样的一个操作,我们可以看到,它的作用也是两点
1.调用BattleValue类的析构函数
2.把BattleValue对象的所有数据都删除
只有这么做,我们才能做到所谓的我们想要的结果,那就是一个Role对象一个BattleValue对象,Role死BattleValue死。Role生BattleValue生。
其实这是为了方便理解,若吾早知如此,那完全可以不用这么麻烦,直接存一个BattleValue对象在Role类当中,不要放指针,这样C++会帮我们处理内存。当然,前提是需求是
一个Role对象一个BattleValue对象,Role死BattleValue死。Role生BattleValue生。但如果需求不是呢?如果所有的角色当中,弓箭手用一套战斗力,战士用一套战斗力,法师用一套战斗力。。。。。。那么你每一个角色对象都给他一个战斗力子对象对内存来说是不是太浪费了?
这个时候又该说,还是指针实在!但是内存又要如何管理了?
都说C++内存管理是个大坑。实际上也确实是这样。