电子说
>>> 背景
最近在看一些C++资料的过程中,说到在初始化列表中使用关键字new来分配新内存不是异常安全的,应该使用运算符new。这里就引发了我对C++ new的全新认识。
class A {
public:
A(int a) : px(new int(a)) {} // 当然这里基础类是不会异常的
int* px;
};
>>> 内容
首先我们来回忆一下最常规的new用法。首先创建一个测试类A,并在其构造和析构函数里面打印提示语句。可以发现,在new一个A对象的时候调用了构造函数,在delete一个A对象的时候析构了此对象。而且与C标准库中的malloc函数相比,new关键字不需要知道分配的字节数,而是对类型大小做了自动推断,显然更加方便。
class A {
public:
A(int a) : _a(a) {
cout < < "object A(" < < _a < < ") is constructed.n";
}
~A() {
cout < < "object A(" < < _a < < ") is destructed.n";
}
int _a;
};
int main() {
A* obj = new A(1);
delete obj;
return 0;
}
object A(1) is constructed.
object A(1) is destructed.
但是方便的代价就是隐藏了很多细节,从而可能导致使用者在没有充分理解的情况下造成误用。其实关键字new做了非常多的操作。
这里首先给出3个概念,分别是:关键字new、操作符new和放置new(或者说,keywords new、operator new、placement new)。它们之间的关系大概如下所示。
当我们使用关键字new去创建一个对象时,会首先根据A类型推断出需要申请的内存字节数,然后再交给operator new去按字节数申请一块可用的内存(否则抛出异常),最后调用类的构造函数创造一个对象保存在申请的这段内存中。
而placement new起到的作用是在分配好的内存上创建对象,和operator new有那么一点互补的意思。placement new的引入是为了避免一些频繁的内存申请和回收操作,可以专门申请一块内存做重复的计算,而不是需要一个对象就申请一个内存,从而提高效率。
下面是一个比较综合的例子,来表现这些new之间不同。可以看到,我们首先通过operator new来创建一块能够容纳3个A对象的内存空间,然后通过placement new来在这个申请好的内存空间上创建对象,最后使用operator delete把申请的空间销毁。
可以看到operator new不会调用构造函数,operator delete也不会调用析构函数。通过placement new配合起始指针的偏移,可以逐个在新内存上创建有意义的数据对象。
int main() {
A* mempool = (A*)operator new(sizeof(A) * 3);
cout < < "Memory is allocated!n";
A* obj1 = new(mempool) A(1);
A* obj2 = new(mempool + 1) A(2);
A* obj3 = new(mempool + 2) A(3);
cout < < obj1- >_a < < endl;
cout < < obj2- >_a < < endl;
cout < < obj3- >_a < < endl;
operator delete(mempool);
cout < < obj1- >_a < < endl;
cout < < obj2- >_a < < endl;
cout < < obj3- >_a < < endl;
return 0;
}
Memory is allocated!
object A(1) is constructed.
object A(2) is constructed.
object A(3) is constructed.
1
2
3
35134640
0
1448320
>>> 小结
可以看出,C++在内存分配引入了不少的概念,operator new和operator delete都是可以被自定义类重载的,这就给予了程序员很好的自由度。除了使用new和delete来管理内存外,C++还引入了更为复杂的allocator(或分配器)的概念。
全部0条评论
快来发表一下你的评论吧 !