作者:储兰兰快乐 | 来源:互联网 | 2023-09-25 11:36
一、new/delete
在c++中新增了new/delete关键字,当我们定义了一个类Class Test;时,auto test = new TestNew() 这条语句将会执行Test的构造函数默认构造函数,例如:
class TestNew
{
public:TestNew(){_i &#61; 0;_c &#61; 0;std::cout <<_i <<_c;}~TestNew(){}private:int _i;float _c;
};int main()
{auto test_new &#61; new TestNew();return 0;
}
实际上 new和delete操作都分为两步&#xff0c;new 操作首先 malloc sizeof(TestNew)大小的内存空间&#xff0c;再去执行TestNew()构造函数。同时delete test_new 操作是先执行~TestNew(&#xff09;操作&#xff0c;再执行free&#xff08;test_new&#xff09;操作。 所以导致了一些问题的出现&#xff0c;举例如下&#xff1a;
class TestNew
{
public:TestNew(){_i &#61; new int[10];_c &#61; 0;std::cout <<"TestNew()" <};int main()
{void *p &#61; new TestNew();delete p;return 0;
}
运行结果&#xff1a;
该代码用void* 指针去指向 new TestNew()&#xff0c; 导致调用的构造函数&#xff0c;而没有调用析构函数&#xff0c;同时构造函数中分配的内存&#xff0c;在析构函数中没有正确执行&#xff0c;导致了内存泄漏。
二、常见用法
而如下结构是经常用的&#xff1a;
struct Data
{~Data(){delete _data;_size &#61; 0;}void *_data; // 指针int _size; // 大小
};template
void AddData(std::list &list_data)
{void* t &#61; new T();Data *data &#61; new Data();data._data &#61; t;data._size &#61; sizeof(T);list_data.push_back(data);
}int main()
{std::list list_data;AddData(list_data);AddData(list_data);AddData(list_data);for (auto iter &#61; list_data.begin(); iter !&#61; list_data.end(); iter&#43;&#43;){delete *iter;}return 0;
}
实现一个可以增加任何数据的list&#xff0c;此时int和double是可以正常释放的&#xff0c;而TestNew是不能释放成功&#xff0c;能够正常释放的空间只有
sizeof(TestNew);如何解决这个问题成为不定参数列表的关键。事实上stl中的内存分配都不通过new/delete来进行&#xff0c;因为这本来就两步操作&#xff0c;stl分配方式如下&#xff1a;
- allocator与类绑定&#xff0c;因为allocator是一个泛型类
- allocate()申请指定大小空间
- construct()构建对象&#xff0c;其参数为可变参数&#xff0c;所以可以选择匹配的构造函数
- 使用&#xff0c;与其它指针使用无异
- destroy()析构对象&#xff0c;此时空间还是可以使用
- deallocate()回收空间
虽然增加了一步&#xff0c;但大体流程还是先分配内存&#xff0c;再构造对象。
三、解决方案&#xff08;2019.6.26增加&#xff09;
既然是因为无法调用到析构函数导致内存泄漏&#xff0c;所有我们可以采取手动去调用析构函数的方法&#xff0c;定义一个泛型类&#xff0c;包装需要构造的函数&#xff0c;同时增加一个头指针&#xff0c;指向析构函数&#xff0c;在消耗这个对象时&#xff0c;可以先手动执行析构函数&#xff0c;再释放内存&#xff08;具体实现待添加...&#xff09;
template
struct data_construct_child;
struct data_construct
{templatestatic int size(){return sizeof(T) &#43; sizeof(void*);}templatestatic void* construct(const T& data, void *memory &#61; nullptr){if (memory &#61;&#61; nullptr) memory &#61; new char[size()];*((void **)memory) &#61; data_construct_child::get_instance();char *p_data &#61; (char*)memory &#43; sizeof(void*);new (p_data) T(data);return (void*)memory;}templatestatic T& get_value(void *p){T *t &#61; (T*)((char*)p &#43; sizeof(void*));return *t;}static void destruct(void *memory, bool delete_memory &#61; true){data_construct *constructs &#61; (data_construct *)*((void**)memory);constructs->call_destruct((char*)memory &#43; sizeof(void*));if (delete_memory) delete[] memory;}virtual void call_destruct(void *p) &#61; 0;
};template
struct data_construct_child : public data_construct
{static data_construct_child * get_instance(){static data_construct_child instance;return &instance;}virtual void call_destruct(void *p){T *t &#61; (T*)p;t->~T();}};class TestNew
{
public:TestNew(){_i &#61; new int[10];_c &#61; 0;std::cout <<"TestNew()" <};struct Data
{~Data(){data_construct::destruct(_data);_size &#61; 0;}void *_data; // 指针int _size; // 大小
};template
void AddData(std::list &list_data)
{T t;void* pp &#61; data_construct::construct(t);auto data &#61; new Data();data->_data &#61; pp;data->_size &#61; data_construct::size();list_data.push_back(data);
}int main()
{std::list list_data;AddData(list_data);AddData(list_data);AddData(list_data);for (auto iter &#61; list_data.begin(); iter !&#61; list_data.end(); iter&#43;&#43;){delete *iter;}return 0;
}
通过一个模板类&#xff0c;在封装分配内存和执行构造、析构函数两步&#xff0c;达到完美内存释放的目的
运行结果如下&#xff1a;
从结果上看&#xff0c;已经实现调用了析构函数&#xff0c;再删除内存&#xff0c;这两步&#xff0c;同时也可以只调用析构函数不删除对象内存。