c++常规new和定位(placement)new

本文探讨了定位new运算符在C++中的使用方法及其注意事项,包括如何正确地使用定位new来初始化对象,以及如何手动调用析构函数来释放内存。通过示例展示了常见的错误及其解决方案。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

定位放置new操作的语法形式不同于普通的new操作。例如,一般都用如下语句A* p=new A;申请空间,而定位放置new操作则使用如下语句A* p=new (ptr) A;申请空间,其中ptr就是程序员指定的内存首地址。

通常,定位new负责在堆中找到一个足以能满足要求的内存块。new运算符还有另外一种变体,被称为定位(placement)new运算符,他让您能够制定要使用的位置。

例子如下:

#include<iostream>
#include<string>
#include<new>

using namespace std;
const int BUF = 512;

class JustTesting
{
private:
	string words;
	int number;

public:
	JustTesting(const string &s = "Just Testing", int n = 0)
	{
		words = s;
		number = n;
		cout << words << " constructed\n";
	}
	~JustTesting()
	{
		cout << words << " destroyed\n";
	}

	void Show()const
	{
		cout << words << ", " << number << endl;
	}
};

int main()
{
	char * buffer = new char[BUF];

	JustTesting *pc1, *pc2;

	pc1 = new (buffer) JustTesting;
	pc2 = new JustTesting("heap1", 20);

	cout << "Memory block addresses:\n" << "buffer: " << (void *)buffer << "  heap: " << pc2 << endl;
	cout << "Memory contents:\n";
	cout << pc1 << ": ";
	pc1->Show();
	cout << pc2 << ": ";
	pc2->Show();

	JustTesting *pc3, *pc4;
	pc3 = new(buffer) JustTesting("Bad Idea", 6);
	pc4 = new JustTesting("Heap2", 10);

	cout << "Memory contents:\n";
	cout << pc3 << ": ";
	pc3->Show();
	cout << pc4 << ": ";
	pc4->Show();

	delete pc2;
	delete pc4;
	delete []buffer;

	cout << "Done\n";

	return 0;
}
这个程序出现两个问题。第一:在创建第二个对象时,定位new运算符使用第一个新对象来覆盖用于第一个对象的内存单元。显然,如果类动态地为其成员分配内存,将引发问题。

第二:将delete用于pc2和pc4时,将自动调用为pc2和pc4指向的对象调用析构函数;然而,将delete[]用于buffer时,不会使用定位new运算符创建的对象调用析构函数。

不能使用delete pc1;delete pc3。原因在于delete可以和常规new运算符配合使用,但是不能和定位new运算符配合使用。

代码二:

#include<iostream>
#include<string>
#include<new>
using namespace std;
const int BUF = 512;

class JustTesting {
private:
	string words;
	int number;

public:
	JustTesting(const string &s = "Just Testing", int n = 0)
	{
		words = s;
		number = n;
		cout << words << " constructed\n";
	}

	~JustTesting() 
	{
		cout << words << " destroyed\n";
	}

	void Show()const
	{
		cout << words << ", " << number << endl;
	}
};

int main()
{
	char * buffer = new char[BUF];

	JustTesting *pc1, *pc2;
	pc1 = new (buffer)JustTesting;
	pc2 = new JustTesting("Heap1", 20);
	
	cout << "Memory block addresses:\n" << " buffer: " << (void*)buffer << " heap: " << pc2 << endl;
	cout << "Memory contents:\n";
	cout << pc1 << ": ";
	pc1->Show();
	cout << pc2 << ": ";
	pc2->Show();

	JustTesting *pc3, *pc4;
	pc3 = new(buffer + sizeof(JustTesting)) JustTesting("Better Idea", 6);
	pc4 = new JustTesting("Heap2", 10);

	cout << "Memory contents:\n";
	cout << pc3 << ":";
	pc3->Show();
	cout << pc4 << ": ";
	pc4->Show();

	delete pc2;
	delete pc4;

	pc3->~JustTesting();
	pc1->~JustTesting();

	delete[]buffer;
	cout << "Done\n";

	while (1);
	return 0;
}
解决这种问题方案是,显式地调用析构函数,必须指定要销毁的对象。需要注意一点是,正确的删除顺序。应以与创建顺序相反的顺序进行删除。原因在于,晚创建的对象可以依赖于早创建的对象。对象创建完成过后,才能释放用于存储这些对象的缓冲区。

<think>嗯,用户问的是placement newoperator newC++中的区别。我需要先回忆一下这两个概念的不同之处。根据提供的引用资料,operator new有三种形式:普通的抛出异常的版本,nothrow版本,还有placement new。而placement new其实是operator new的第三种形式,也就是带有额外参数的版本。用户可能对这两者的关系区别不太清楚。 首先,我需要明确operator new的基本作用。operator newC++中用于动态分配内存的函数,通常与new表达式相关。当使用new关键字创建一个对象时,编译器会先调用operator new来分配内存,然后调用构造函数初始化对象。例如,引用[1]提到operator new有三种形式,其中第三种是placement new,它不分配内存,而是在已分配的内存上构造对象。 接下来,placement new的具体用途是什么。根据引用[2],placement new允许在已存在的内存地址上构造对象。比如,用户可能有一块预先分配好的内存,想要在这个位置创建对象,这时就可以用placement new。例如,代码中的new (&x) X(5)就是在x的地址上构造一个新的X对象,覆盖原有的值。这跟普通的operator new不同,普通的operator new会分配新的内存。 用户可能混淆的是operator newnew操作符的区别。引用[3]提到,new操作符(new operator)是C++的关键字,它执行两个步骤:调用operator new分配内存,然后调用构造函数。而operator new仅仅负责内存分配,可以被重载。placement new则是operator new的一个特殊重载版本,它接受额外的参数,比如指针,来指定内存位置。 另外,用户可能关心如何释放placement new分配的内存。由于placement new并不分配内存,只是构造对象,所以不需要调用delete来释放内存。但需要显式调用对象的析构函数,然后根据需要处理原始内存,比如如果是栈上的内存,不需要释放,如果是手动管理的堆内存,可能需要其他方式释放。引用[2]也提到了这一点,指出需要手动调用析构函数。 还需要注意,operator new可以被重载,但根据引用[3],每个重载的operator new必须有一个用户定义类型的参数,这可能影响用户自定义内存管理的方式。 总结一下,区别主要在于功能使用场景:operator new负责分配内存,placement new是它的一个版本,用于在指定内存构造对象。而new操作符则是组合了内存分配构造的过程。需要明确每个步骤的责任,以及如何正确管理内存对象生命周期。</think>### Placement new与operator new的区别解析 #### 一、核心概念定义 1. **operator new** operator newC++中负责内存分配的基础函数,有三种形式: ```cpp void* operator new(size_t size); // 基本形式(可能抛出bad_alloc) void* operator new(size_t size, const std::nothrow_t&) noexcept; // 不抛出版本 void* operator new(size_t size, void* ptr) noexcept; // placement形式(不分配内存)[^1] ``` 它只负责内存分配,不涉及对象构造。 2. **placement new** 是operator new的第三种特殊形式,特点: - 在**已分配好的内存地址**上构造对象 - 不进行实际内存分配 - 语法形式:`new (address) Type(args)` #### 二、关键差异对比 | 特性 | operator new | placement new | |---------------------|--------------------|---------------------| | 内存分配 | ✅ 分配新内存 | ❌ 使用现有内存 | | 构造函数调用 | ❌ 仅分配内存 | ✅ 触发构造函数 | | 异常处理 | 可能抛出bad_alloc | 不抛出异常 | | 典型应用场景 | 常规对象创建 | 内存池/对象复用 | | 内存释放方式 | delete操作符 | 需手动调用析构函数 | #### 三、工作流程示例 ```cpp #include <new> class MyClass { public: MyClass(int x) { /* 构造函数 */ } ~MyClass() { /* 析构函数 */ } }; // 使用placement new void demo() { char buffer[sizeof(MyClass)]; // 预分配内存 MyClass* obj = new (buffer) MyClass(10); // placement new构造 obj->~MyClass(); // 必须显式调用析构函数 } ``` #### 四、注意事项 1. 内存生命周期管理需手动控制[^2] 2. 不要对placement new返回的指针使用delete 3. 内存对齐要求需自行保证 4. 与内存池、自定义分配器配合使用时效率最佳 #### 五、典型应用场景 1. 嵌入式系统内存管理 2. 高性能计算中的对象池 3. 自定义内存分配策略 4. 需要精确控制对象位置的特殊需求(如硬件寄存器映射)
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值