运算符 new
new 最常的用法是作为运算符,这时候 new 会在堆上分配一块内存,并会自动调用类的构造函数,如:
string *str = new string("test new");
new 作为运算符时,它是 C++ 内置的,你不能对它做任何的改变,除了使用它。
函数 new
第二种用法是 new 函数,其实 new 作为运算符时,内部分配内存使用的就是 new 函数,其原型是:
void *operator new(size_t size);
new 函数返回的是一个 void 指针,指向一块未经初始化的内存。
可以发现,这和 C 语言的 malloc 行为相似,所以你可以重载new函数,并且增加额外的参数,但是必须保证第一个参数必须是 size_t 类型,它指明了分配内存块的大小。
如果重载了 new 函数,在使用 new 操作符时调用的就是你重载后的 new 函数了。
这时候使用 new 函数,和语句 string *str = new string("test new");
相对的代码大概是如下的样子:
string *str = (string*)operator new(sizeof(string));
str.string("test new"); // 当然这个调用时非法的,但是编译器是没有这个限制的
placement new
placement new 其实也是 new 作为函数的一种用法,它允许你在一块已存在的内存上分配一个对象,而内存上的数据不会被覆盖或者被你主动改写。placement new 同样由 new 操作符调用,调用格式是:
new (buffer) type(size_t size);
先看看下面的代码:
char str[22];
int data = 123;
int *pa = new (&data) int;
int *pb = new (str) int(9);
结果为:
*pa = 123 // 未覆盖原数据)
*pb = 9 // 覆盖原数据
可以看到 placement new 并没有分配新的内存,也可以使用在栈上分配的内存,而不限于堆。
为了使用 placement new 你必须 #include <new>
或 #include <new.h>
。
其实 placement new 和第二种用法一样,只不过多了参数,是函数 new 的重载,语法格式为:
void *operator new(size_t, void* buffer);
它看起来可能是这个样子:
void *operator new(size_t, void* buffer) { return buffer;}
和 new 对应的就是 delete 了。
总结
① 函数 new:
void *operator new(size_t size); // 在堆上分配一块内存
placement new(void *operator new(size_t, void* buffer)); // 在一块已经存在的内存上创建对象
如果你已经有一块内存,placement new 会非常有用,事实上,它在 STL 中有着广泛的使用。
② 运算符 new: 最常用的 new,没什么可说的。
③ 函数 new 不会自动调用类的构造函数,因为它对分配的内存类型一无所知;而运算符 new 会自动调用类的构造函数。
④ 函数 new 允许重载,而 运算符new 不能被重载。
⑤ new 对应的是 delete。