定位new运算符号
//main.cpp
#include<iostream>
#include<string>
using namespace std;
const int BUF = 512;
class JustTest
{
private:
string words;
int number;
public:
JustTest(const string &s = "just testing", int n = 0)
{
words = s;
number = n;
cout << words <<" constructed\n";
}
~JustTest()
{
cout << words << " disconstructed\n";
}
void show()
{
cout << words << "," << number << endl;
}
};
void main()
{
{
char *buffer = new char[BUF];
JustTest *p1 = new(buffer)JustTest;
JustTest *p2 = new JustTest("badidea", 5);
cout << "memory addresses:\n";
cout << "buffer:" << (void*)buffer << endl;
//cout << "buffer:" << p1 << endl;
cout << "heap:" << p2 << endl;
cout << "memory contents:\n";
cout << "buffer:";
p1->show();
cout << "heap:";
p2->show();
//delete p2;
//delete p1;
//delete buffer;
JustTest*p3, *p4;
p3 = new(buffer + sizeof(JustTest))JustTest("badidea2", 6);
p4 = new JustTest("head2", 8);
cout << "memory addresses:\n";
cout << "buffer:" << (void*)buffer << endl;
cout << "heap:" << p4 << endl;
cout << "memory contents:\n";
cout << "buffer:";
p3->show();
cout << "heap:";
p4->show();
delete p2;
delete p4;
delete[]buffer;
}
cout << "Done\n";
}
该程序使用new运算符创建了512字节的内存缓冲区,然后new运算符在堆中创建了两个JustTest对象,并试图使用定位new运算符在内存缓冲区中创建两个JustTest对象,最后使用delete释放new分配的内存。
在使用第二个定位new运算符时存在两个问题:
1.创建第二个对象时,定位new运算符使用一个新的对象来覆盖第一个对象的内存单元。显然如果类动态的为其成员分配内存将引发问题。
2.在delete用于p2和p4时,将自动调用p2和p4的析构函数,而将delete[]用于buffer时,不会为使用定位new运算符创建的对象调用析构函数。这也是我们为什么只看p2和p4的析构信息输出。
因此,在使用定位new运算符时,必须提供两个位于缓冲区的不同地址(确保不重叠):
JustTest *p1 = new(buffer)JustTest;
//添加偏移量
JustTest *p3 = new(buffer+sizeof)JustTest("badidea2", 6);
但是使用定位new运算符分配的对象内存,
delete p1;
delete p3;
是不允许的,原因是delete必须和常规的new运算符配合使用,但不能和定位new运算符配合使用。
指针p1指向的地址和bufeer相同,但是buffer是用new[]初始化的,因此必须使用delete[]来释放。即使buffer是使用new而不是new[]初始化的 ,delete p1也将释放buffer,而不是p1,因为new/delete系统知道分配的512字节块的buffer,但对定位new运算符对该内存块做了何种处理一无所知。
由运行结果可以知道,p1和p3的析构函数并没有调用,这种问题的解决方法是,显示地位定位new运算符创建的对象调用析构函数。正常情况下是自动调用析构函数的,这是显式调用析构函数的少数几种情况之一:
p3->~JustTest();
p1->~JustTest();
需要注意的一点是正确的删除顺序。对于定位new运算符创建的对象,应与与创建顺序相反的顺序进行删除。原因在于:晚创建的对象可能依赖于早创建的对象。另外,必须当所有的对象销毁后,才能释放用于存储对象的缓冲区。