new operator
new operator是我们常用的new。
new 和 delete 是用来在 堆上申请和释放空间的 ,是 C++ 定义的 关键字,和 sizeof 一样。
实际 new / delete 和 malloc / free 最大的区别是,前者对于 自定义类型 除了可以开辟空间,还会调用构造和析构函数
-
分配内存,如果类A重载了operator new,那么将调用A::operator new(size_t )来完成,如果没有重载,就调用::operator new(size_t ),即全局new操作符来完成
-
调用构造函数生成类对象;
-
返回相应指针
1.operator new、operator delete就是对malloc和free的封装
2.operator new中调用malloc开辟空间失败后,改为抛异常处理,这种处理更符合C++处理错误的方式
new 的原理
Ⅰ. 调用 operator new 函数申请空间
Ⅱ. 在申请的空间上执行构造函数,完成对象的构造
delete 的原理
Ⅰ. 在空间上执行析构函数,完成对象中资源的清理工作
Ⅱ. 调用 operator delete 函数释放对象的空间
new T[N] 的原理
Ⅰ. 调用 operator new[] 函数,在 operator new[] 中实际调用 operator new 函数完成 N 个对象空间的申请
Ⅱ. 在申请的空间上执行 N 次构造函数
delete[] 的原理
Ⅰ. 在释放的对象空间上执行 N 次析构函数,完成 N 个对象中资源的清理
Ⅱ. 调用 operator delete[] 释放空间,实际在 operator delete[] 中调用 operator delete 来释放空间
operator new
operator new是一个操作符,和+ -操作符一样,作用是分配空间。我们可以重写它们,修改分配空间的方式。
operator new返回值必须是void*。第一个参数必须是size_t
void* operator new (std::size_t size) throw (std::bad_alloc);
void* operator new (std::size_t size, const std::nothrow_t& nothrow_constant) throw();
在下面的例子中,我们使用重载了三个operator new方法, 并分别调用。
#include <iostream>
#include <string>
#include <malloc.h>
using namespace std;
//student class
class Stu
{
public:
Stu(string name, int age)
{
cout << "call Stu class constructor" << endl;
name_ = name;
age_ = age;
};
public:
void print() const
{
cout << "name = " << name_ << std::endl;
cout<< "age = " << age_ << std::endl;
};
void* operator new(size_t size)
{
std::cout << "call operator new" << std::endl;
return malloc(size);
}
void* operator new(size_t size, int num)
{
std::cout << "call operator new with int" << std::endl;
return malloc(size);
}
void* operator new(size_t size, char c)
{
std::cout << "call operator new with char" << std::endl;
return malloc(size);
}
private:
string name_;
int age_;
};
int main()
{
Stu* stu1 = new Stu("a", 10);
Stu* stu2 = new(1) Stu("a", 10);
Stu* stu3 = new('c') Stu("a", 10);
}
placement new
placement new是operator new的一种重载形式,其作用是可以在指定的内存地址创建对象。
placement new返回值必须是void*。第一个参数必须是size_t, 第二个参数是void*。
void* operator new (std::size_t size, void* ptr) throw();
下面的是一个关于placement new的调用例子:
#include <iostream>
#include <string>
#include <malloc.h>
using namespace std;
//student class
class Stu
{
public:
Stu(string name, int age)
{
name_ = name;
age_ = age;
};
public:
void print() const
{
cout << "name = " << name_ << std::endl;
cout<< "age = " << age_ << std::endl;
};
void* operator new(size_t size, void* p)
{
std::cout << "placement new" << std::endl;
return p;
};
private:
string name_;
int age_;
};
int main()
{
void* stu1 = (Stu*)malloc(sizeof(Stu));
new (stu1) Stu("stu1", 10);
((Stu*)stu1)->print();
}
由于placement new可以在一个指定的位置创建对象,因此在STL中有很广泛的运用, 例子vector容器初始化的时候,会使用allocator申请一定的内存,当使用push_back放入对象时, 就可以使用placement new在申请的位置创建对象。
结论
对于new, operator new 和 placement new三者的区别, 我们总结如下:
new:
new是一个关键字,不能被重载。
new 操作符的执行过程如下:
-
调用operator new分配内存 ;
-
调用构造函数生成类对象;
-
返回相应指针。
operator new:
operator new就像operator + 一样,是可以重载的。
如果类中没有重载operator new,那么调用的就是全局的::operator new来完成堆的分配。
同理,operator new[]、operator delete、operator delete[]也是可以重载的。
placement new:
placement new和operator new并没有本质区别。它们都是operator new操作符的重载,只是参数不相同。
placement并不分配内存,只是返回指向已经分配好的某段内存的一个指针。因此不能删除它,但需要调用对象的析构函数。
如果你想在已经分配的内存中创建一个对象,使用new时行不通的。
也就是说placement new允许你在一个已经分配好的内存中(栈或者堆中)构造一个新的对象。原型中void* p实际上就是指向一个已经分配好的内存缓冲区的的首地址。