More Effective C++ 08:理解各种不同含义的 new 和 delete

string *ps = new string("Memory Management");

当你写这样的代码,你使用的newnew操作符。
这个操作符就像sizeof一样是语言内置的,你不能改变它的含义,它的功能总是一样的。它要完成的功能分成两部分。第一部分是分配足够的内存以便容纳所需类型的对象。第二部分是它调用构造函数初始化内存中的对象.

你所能改变的是如何为对象分配内存new 操作符调用一个函数来完成必需的内存分配,你能够重写或重载这个函数来改变它的行为。new操作符为分配内存所调用函数的名字是operator new

函数operator new通常这样声明:

void * operator new(size_t size);

你一般不会直接调用 operator new,但是一旦这么做,你可以像调用其它函数一样调用它:

void *rawMemory = operator new(sizeof(string));

operator new 将返回一个指针,指向一块足够容纳一个 string 类型对象的内存,它仅仅分配内存,不会调用构造函数。

当你的编译器遇见这样的语句:

string *ps = new string("Memory Management");

它生成的代码或多或少与下面的代码相似:

void *memory = operator new(sizeof(string)); // 得到未经处理的内存为 String 对象 
call string::string("Memory Management") //初始化 
on *memory; // 内存中 
 // 的对象 
string *ps = static_cast<string*>(memory); // 是 ps 指针指向 新的对象

placement new

有时你确实想直接调用构造函数。在一个已存在的对象上调用构造函数是没有意义的,因为构造函数用来初始化对象,而一个对象仅仅能在给它初值时被初始化一次。但是有时你有一些已经被分配但是尚未处理的(raw)内存,你需要在这些内存中构造一个对象。你可以使用一个特殊的operator new ,它被称为 placement new
下面是使用例子:

class Widget 
{ 
public: 
	Widget(int widgetSize); 
	... 
}; 
Widget * constructWidgetInBuffer(void *buffer, int widgetSize) 
{ 
	 return new (buffer) Widget(widgetSize); 
}

这个函数返回一个指针,指向一个Widget对象,对象在转递给函数的 buffer 里分配。

new (buffer) Widget(widgetSize)

这初看上去有些陌生,但是它是new操作符的一个用法,需要使用一个额外的变量(buffer),当 new 操作符隐含调用 operator new 函数时,把这个变量传递给它。被调用的 operator new函数除了待有强制的参数 size_t 外,还必须接受 void*指针参数,指向构造对象占用的内存空间。
这个 operator new 就是 placement new,它看上去像这样:

void * operator new(size_t, void *location) 
{ 
	return location; 
}

是这就是 placement new 需要做的事情。毕竟 operator new的目的是为对象分配内存然后返回指向该内存的指针。在使用 placement new 的情况下,调用者已经获得了指向内存的指针,因为调用者知道对象应该放在哪里。placement new 必须做的就是返回转递给它的指针
**

总结

你想在堆上建立一个对象,应该用 new 操作符。它既分配内存又为对象调用构造函数。

如果你仅仅想分配内存,就应该调用 operator new 函数;它不会调用构造函数。

如果你想定制自己的在堆对象被建立时的内存分配过程,你应该写你自己的 operator new 函数,然后使用 new 操作符,new 操作符会调用你定制的 operator new

如果你想在一块已经获得指针的内存里建立一个对象,应该用 placement new

删除和内存回收

函数 operator deletedelete 操作符的关系与 operator newnew 操作符的关系一样。
考虑一下代码:

string *ps; 
... 
delete ps;

你的编译器会生成代码来析构对象并释放对象占有的内存。
Operator delete 用来释放内存,它被这样声明:

void operator delete(void *memoryToBeDeallocated);
delete ps; 

导致编译器生成类似于这样的代码:

ps->~string(); 
operator delete(ps);

如果你只想处理未被初始化的内存,你应该绕过 newdelete操作符,而调用 operator new 获得内存和 operator delete 释放内存给系统:

void *buffer = operator new(50*sizeof(char)); // 分配足够的 内存以容纳 50 个 char 
 //没有调用构造函数 
... 
operator delete(buffer); // 释放内存 
 // 没有调用析构函数

与在 C 中调用 mallocfree 等同。

数组

到目前为止我们所测试的都是一次建立一个对象。怎样分配数组?会发生什么?

string *ps = new string[10];

被使用的 new 仍然是 new 操作符,但是建立数组时 new 操作符的行为与单个对象建立有少许不同。第一是内存不再用 operator new 分配,代替以等同的数组分配函数,叫做operator new[]
它与 operator new 一样能被重载。这就允许你控制数组的内存分配,就像你能控制单个对象内存分配一样

string *ps = new string[10];
调用 operator new[]10 个 string 对象分配内存,
然后对每个数组元素调用string 对象的默认构造函数。

就像你能替换或重载 operator delete 一样,你也替换或重载 operator delete[]

猜你喜欢

转载自blog.csdn.net/qq_44800780/article/details/106139991