定位new表达式
在已分配的原始内存空间中调用构造函数初始化一个对象
格式:
new(place_address) type 或者new(place_address) type(initializer_list)
其中place_address必须是一个指针,initializer_list是类型的初始化列表
使用场景:
一般配合内存池使用,因为内存池分配出的内存没有初始化,所以如果是自定义类型的对象,需要使用new的定义表达式进行显式调用构造函数进行初始化
void Test()
{
Test* pt = (Test*)malloc(sizeof(Test));
new(pt) Test; //如果Test类的构造函数有参数时此处则需要传参
}
对象创建的方式
在C++中类的对象创建分两种,一种是静态的建立如A a,另一种是动态建立,如A* ptr = new A;
区别:
- 静态建立一个对象,是由编译器为对象在栈空间中分配内存。是通过移动栈顶指针挪出适当的空间然后在这片内存空间上调用构造函数形成一个栈对象(直接调用构造函数)
- 动态建立类对象,是使用new运算符将对象建立在堆空间中
- 执行operator new()函数,在堆空间中分配合适空间
- 调用构造函数构造对象,初始化这片内存空间
由此引出了一个经典的面试题:
- 创建一个类,只能在堆上创建对象
- 创建一个类,只能在栈上创建对象
一.只能在堆上创建对象
-
方法一:容易想到将构造函数私有化
构造函数私有化之后无法在类外部调用构造函数来构造类对象,只能用new运算符来建立对象,但是new运算符实际底层分两步,在调用构造函数时因其已私有化所以无法调用
-
方法二:
当对象建立在栈上面时是由编译器分配内存空间,调用构造函数来构造栈对象,当对象使用完之后编译器会调用析构函数来释放栈对象所占的空间
假设类的析构函数私有,编译器无法调用析构函数来释放内存。所以**在为类对象分配栈空间时会先检查类的析构函数的访问性。**如果类的析构函数是私有的则编译器不会在栈空间上为类对象分配内存
class A { public: A(){} void destroy(){delete this;} private: ~A(){} };
-
提供一个static函数来完成构造
class A { public: static A* create() { return new A(); } void destroy() { delete this; } private: A(){} ~A(){} A(const A&) = delete; //将编译器默认生成的拷贝构造函数删除掉,防止别人 //调用拷贝构造函数生成对象 }
二.只能在栈上创建对象
只有使用new运算符对象才会建立在堆上。因此只要禁用new运算符就可以实现类对象只能建立在栈上,将operator new()设为私有即可。