C++语言析构函数与内存池
小标 2018-07-10 来源 : 阅读 786 评论 0

摘要:本文主要向大家介绍了C++语言析构函数与内存池,通过具体的内容向大家展示,希望对大家学习C++语言有所帮助。

本文主要向大家介绍了C++语言析构函数与内存池,通过具体的内容向大家展示,希望对大家学习C++语言有所帮助。

C++ Primer 书中也提到编写 class 时要注意 copy control 成员(拷贝构造函数,赋值操作符,析构函数, C++11 又多个移动构造函数)。工作时在 C++ 和 C# 之间切换,有时就忘记了 C++ 的细节(真的好讨厌)。 C++ 析构函数与构造函数对应,构造对象时调用构造函数,析构对象时调用析构函数,于是可以在对象的析构函数中释放资源。 C++ class type 对象的生命期可以由程序完全控制,而 C# 引用类型对象是被托管的,万一处理到关键任务时 GC 造成卡顿一下,也是蛮郁闷的。这篇博客主要总结一下 C++ 析构函数,以下内容参考《 C++ Primer 4th 》 13.3 The Destructor 小节。

■ C++ 的析构函数在哪些情况下会执行呢?

· The destructor is run only when a pointer to a dynamically allocated object is deleted or when an actual object (not a reference to the object) goes out of scope.

· The destructor is not run when a reference or a pointer to an object goes out of scope.

· Destructors are also run on the elements of class type in a container whether a library container or built-in array when the container is destroyed.

前两点没有什么好说的,对第三点提一下。当 STL 容器或者内部数组中存放的是 class type 对象元素,那么当容器或者数组销毁时,各元素的析构函数也会被执行(如果自己编写容器类,不要忘记这个功能点)。如下代码。

{

    Foo *p = new Foo[10];

    vector<Foo> vec(p, p + 10);

    delete p; // array is freed, destructor run on each element

    // vec goes out of scope, call destructor on each element

}

但是后面紧接着说,容器中的元素逆序被析构,也就说 size() - 1 这个元素最先被析构。我翻了一下 msys2 平台 g++ 6.2.0 版本中的 vector 源码,发现它的析构函数最终会调用下面的函数(文件 stl_construct.h ),反而是从 0 索引开始析构的。所以书上这句话看看就行了,别当真。我测试了一下,数组中的元素倒是按逆序被析构的。

template<bool>struct _Destroy_aux

{

    template<typename _ForwardIterator>

    static void

    __destroy(_ForwardIterator __first, _ForwardIterator __last)

    {

      for (; __first != __last; ++__first)

        std::_Destroy(std::__addressof(*__first));

    }

};

■ 什么时候需要编写类的析构函数呢?

通常情况下并不需要为类编写析构函数,如果需要在对象析构时处理一些事情,比如释放资源,那么就需要编写析构函数。书中提到 Rule of Three 就是指,如果一个类需要析构函数,那么就还需要 copy control 其他成员(拷贝构造函数,赋值操作符, C++11 的移动构造函数)。
然后书中提到编译器总会合成一个析构函数。关于这个合成的析构函数有如下要点。

· Unlike the copy constructor or assignment operator, the compiler always synthesizes a destructor for us. The synthesized destructor destroys each nonstatic member in the reverse order from that in which the object was created.

· For each member that is of class type, the synthesized destructor invokes that member's destructor to destroy the object.

· Destroying a member of built-in or compound type has no effect. In particular, the synthesized destructor does not delete the object pointed to by a pointer member.

· An important difference between the destructor and the copy constructor or assignment operator is that even if we write our own destructor, the synthesized destructor is still run.

第二点是编译器合成的析构函数会自动执行 class type 成员的析构函数。第三点中的 built-in type 指 int float 等这种类型, compound type 指指针,引用这种类型,编译器合成的析构函数对这两种类型没有影响。由于编译器合成的析构函数负责成员变量的析构工作,对第四点就会觉得很理所当然,那就是虽然类中定义了析构函数,但是编译器合成的析构函数还是会执行。

 

■ 一个简单的内存池

经过上面的总结,我们知道 C++ 构造函数和析构函数完全与对象的生命周期同步。那么开发 C++ 内存池时,如何在已经分配好的内存空间上构造对象和析构对象呢。
一个内存池的基本逻辑有:内存池的空间管理、对象的构造、对象的回收、标识已分配对象的唯一 handle 。
下面举了一个简单的 MemObject ,只能构造一个对象,这样逻辑会很简单,让我们专注于对象的构造和回收。在 Alloc 时会在已经分配的内存上调用对象的习惯函数,在 Free 时会调用对象的析构函数,再设置 use_ 为 false ,使得这块内存再次被使用。

#include <new>#include <stdio.h>#include <stdlib.h>

template<typename T>class MemObject {public:

MemObject()

: use_(false), handle_(0) {

pObj_ = static_cast<T*>(malloc(sizeof(T)));

}

 

~MemObject() {

free(pObj_);

}

 

T* Alloc(unsigned int &hdl) {

if (use_) {

printf("object is using\n");

return NULL;

}

hdl = ++handle_;

use_ = true;

new(pObj_) T();

return pObj_;

}

 

void Free(unsigned int hdl) {

if (!use_ || hdl != handle_) {

printf("invalid free, use:%d hdl:%d\n", use_, handle_);

return;

}

use_ = false;

pObj_->~T();

}

 

T* Get(int hdl) {

if (!use_ || hdl != handle_) {

printf("invalid get, use:%d hdl:%d\n", use_, handle_);

return NULL;

}

return pObj_;

}

private:

MemObject(const MemObject&) {}

MemObject& operator=(const MemObject&) {}

 

T *pObj_;

bool use_;

unsigned int handle_;

};

struct Foo {

Foo() {

printf("Foo ctor\n");

}

 

~Foo() {

printf("Foo ~ctor\n");

}

 

void Test() {

printf("Test in Foo\n");

}

};

int main() {

MemObject<Foo> mo;

Foo *ptr;

unsigned int hdl1, hdl2;

 

ptr = mo.Alloc(hdl1);

ptr->Test();

mo.Free(hdl1);

 

printf("\n");

mo.Free(hdl1);

ptr = mo.Alloc(hdl2);

ptr->Test();

mo.Alloc(hdl1);

mo.Free(hdl2);

return 0;

}

编译 g++ -o t test.cpp 后,运行结果如下。

$ ./t.exe

Foo ctor

Test in Foo

Foo ~ctor

 

invalid free, use:0 hdl:1

Foo ctorTest in Fooobject is using

Foo ~ctor

本文由职坐标整理并发布,希望对同学们有所帮助。了解更多详情请关注职坐标编程语言C/C+频道!

本文由 @小标 发布于职坐标。未经许可,禁止转载。
喜欢 | 1 不喜欢 | 0
看完这篇文章有何感觉?已经有1人表态,100%的人喜欢 快给朋友分享吧~
评论(0)
后参与评论

您输入的评论内容中包含违禁敏感词

我知道了

助您圆梦职场 匹配合适岗位
验证码手机号,获得海同独家IT培训资料
选择就业方向:
人工智能物联网
大数据开发/分析
人工智能Python
Java全栈开发
WEB前端+H5

请输入正确的手机号码

请输入正确的验证码

获取验证码

您今天的短信下发次数太多了,明天再试试吧!

提交

我们会在第一时间安排职业规划师联系您!

您也可以联系我们的职业规划师咨询:

小职老师的微信号:z_zhizuobiao
小职老师的微信号:z_zhizuobiao

版权所有 职坐标-一站式IT培训就业服务领导者 沪ICP备13042190号-4
上海海同信息科技有限公司 Copyright ©2015 www.zhizuobiao.com,All Rights Reserved.
 沪公网安备 31011502005948号    

©2015 www.zhizuobiao.com All Rights Reserved

208小时内训课程