类和动态内存分配(3)

定位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运算符创建的对象,应与与创建顺序相反的顺序进行删除。原因在于:晚创建的对象可能依赖于早创建的对象。另外,必须当所有的对象销毁后,才能释放用于存储对象的缓冲区。

猜你喜欢

转载自blog.csdn.net/qq_29689907/article/details/84449717