
在 C++ 中,内存管理是性能优化和资源控制的核心。除了标准的new/delete操作符外,C++ 还提供了两种高级机制:定位 new 表达式(Placement New)和类特定的new/delete表达式(Class-specific new/delete)。
定位 new(Placement New)是 C++ 的一种特殊语法,允许在 已分配的原始内存(Raw Memory)中直接构造对象。与普通new不同,它不负责内存分配,仅执行对象的构造过程。
核心特点:
<new>头文件。定位 new 的语法如下:
#include <new> // 必须包含此头文件
// 在已分配的内存p上构造类型T的对象
T* obj = new (p) T(构造参数);其中:
p是指向已分配内存的指针(通常是void*类型)。T是要构造的对象类型。构造参数是传递给T构造函数的参数(可选)。普通new的执行流程包含两步:
operator new分配内存(可能抛出std::bad_alloc异常)。而定位 new 跳过了第一步,直接在已有内存上构造对象。其典型应用场景包括:
malloc/free)。operator new实现更灵活的内存分配策略。场景描述:假设我们需要频繁创建和销毁大量小对象(如游戏中的粒子),使用普通new会导致内存碎片和性能下降。通过内存池预先分配连续内存块,再用定位 new 构造对象,可以显著提升效率。
实现代码:
#include <iostream>
#include <new>
#include <vector>
// 内存池类(简化版)
class MemoryPool {
private:
char* pool; // 内存池起始地址
size_t block_size; // 每个内存块的大小
size_t block_num; // 内存块数量
bool* used; // 记录内存块是否被使用
public:
// 构造函数:初始化内存池
MemoryPool(size_t block_size, size_t block_num)
: block_size(block_size), block_num(block_num) {
// 分配连续内存(对齐处理:简化示例,实际需考虑对齐)
pool = new char[block_size * block_num];
used = new bool[block_num]{false}; // 初始化为未使用
}
// 分配一个内存块(返回原始内存地址)
void* allocate() {
for (size_t i = 0; i < block_num; ++i) {
if (!used[i]) {
used[i] = true;
return pool + i * block_size;
}
}
return nullptr; // 内存池已满
}
// 释放一个内存块
void deallocate(void* p) {
if (p < pool || p >= pool + block_size * block_num) return;
size_t index = (static_cast<char*>(p) - pool) / block_size;
used[index] = false;
}
// 析构函数:释放内存池
~MemoryPool() {
delete[] pool;
delete[] used;
}
};
// 测试类:需要频繁创建的对象
class Particle {
private:
int id;
public:
Particle(int id) : id(id) {
std::cout << "Particle " << id << " 构造完成" << std::endl;
}
~Particle() {
std::cout << "Particle " << id << " 析构完成" << std::endl;
}
void print() const {
std::cout << "Particle " << id << " 正在运行" << std::endl;
}
};
int main() {
// 初始化内存池:每个块64字节(足够容纳Particle),共10个块
MemoryPool pool(64, 10);
// 使用定位new构造对象
std::vector<Particle*> particles;
for (int i = 0; i < 5; ++i) {
void* mem = pool.allocate();
if (!mem) {
std::cerr << "内存池不足!" << std::endl;
break;
}
// 在内存池的内存上构造Particle对象
Particle* p = new (mem) Particle(i);
particles.push_back(p);
}
// 调用对象方法
for (auto p : particles) {
p->print();
}
// 显式调用析构函数(定位new不会自动调用析构)
for (auto p : particles) {
p->~Particle(); // 必须手动调用析构!
pool.deallocate(p); // 释放内存块回内存池
}
return 0;
}内存池类MemoryPool:
pool),并通过used数组标记每个内存块的使用状态。allocate()方法查找空闲内存块并返回其地址。deallocate()方法将内存块标记为空闲。测试类Particle:构造函数和析构函数打印日志,方便观察生命周期。
主函数:
Particle对象。~Particle())并释放内存块回内存池(否则会导致内存泄漏)。运行结果:

obj->~T()释放资源(如关闭文件、释放堆内存等)。alignas(16)),内存池分配的内存必须满足对齐要求(可通过std::align或自定义对齐分配实现)。new/delete表达式new/delete?默认情况下,new/delete使用全局的operator new/operator delete分配内存,这可能无法满足某些场景的需求:
通过重载类的operator new和operator delete,可以为特定类定制内存分配逻辑。
类特定的operator new和operator delete是类的静态成员函数(无需实例化即可调用),其语法如下:
class MyClass {
public:
// 普通版本:分配单个对象
static void* operator new(size_t size);
static void operator delete(void* p, size_t size);
// 数组版本:分配对象数组
static void* operator new[](size_t size);
static void operator delete[](void* p, size_t size);
// 可选:带额外参数的版本(如对齐)
static void* operator new(size_t size, std::align_val_t align);
static void operator delete(void* p, std::align_val_t align);
};size参数:由编译器自动传递,代表需要分配的内存大小(对于普通对象是sizeof(MyClass),数组是sizeof(MyClass)*N + 额外开销)。align_val_t参数:C++17 引入,用于指定对齐要求(当类使用alignas修饰时自动传递)。operator new的行为兼容(如分配失败时抛出std::bad_alloc异常)。场景描述
设计一个Widget类,要求:
实现代码
#include <iostream>
#include <new>
#include <vector>
#include <cstdalign> // 对齐相关头文件
// 全局统计信息
struct AllocStats {
size_t count = 0; // 分配次数
size_t total_bytes = 0; // 总分配字节数
};
// 内存池(支持对齐)
class AlignedMemoryPool {
private:
char* pool;
size_t block_size;
size_t block_num;
bool* used;
std::align_val_t align; // 对齐要求
public:
AlignedMemoryPool(size_t block_size, size_t block_num, std::align_val_t align)
: block_size(block_size), block_num(block_num), align(align) {
// 分配对齐内存(使用std::aligned_alloc)
pool = static_cast<char*>(std::aligned_alloc(static_cast<size_t>(align), block_size * block_num));
used = new bool[block_num]{false};
}
void* allocate() {
for (size_t i = 0; i < block_num; ++i) {
if (!used[i]) {
used[i] = true;
return pool + i * block_size;
}
}
return nullptr;
}
void deallocate(void* p) {
if (p < pool || p >= pool + block_size * block_num) return;
size_t index = (static_cast<char*>(p) - pool) / block_size;
used[index] = false;
}
~AlignedMemoryPool() {
std::free(pool); // 对齐内存用std::free释放
delete[] used;
}
};
// 带内存池和统计的Widget类
class Widget {
private:
int data;
static AllocStats stats; // 分配统计
static AlignedMemoryPool pool; // 类共享的内存池
public:
// 构造函数
Widget(int data) : data(data) {}
// 析构函数
~Widget() {}
// 重载operator new(普通版本)
static void* operator new(size_t size) {
void* mem = pool.allocate();
if (!mem) {
throw std::bad_alloc(); // 分配失败抛出异常
}
// 更新统计信息
stats.count++;
stats.total_bytes += size;
return mem;
}
// 重载operator delete(普通版本)
static void operator delete(void* p, size_t size) {
pool.deallocate(p);
// 更新统计信息
stats.count--;
stats.total_bytes -= size;
}
// 重载operator new(对齐版本,C++17+)
static void* operator new(size_t size, std::align_val_t align) {
// 这里简化处理:假设内存池已按align对齐
void* mem = pool.allocate();
if (!mem) {
throw std::bad_alloc();
}
stats.count++;
stats.total_bytes += size;
return mem;
}
// 重载operator delete(对齐版本)
static void operator delete(void* p, std::align_val_t align) {
pool.deallocate(p);
stats.count--;
stats.total_bytes -= align; // 简化处理,实际应使用size参数
}
// 打印统计信息
static void printStats() {
std::cout << "Widget分配统计:" << std::endl
<< " 总分配次数: " << stats.count << std::endl
<< " 总分配字节数: " << stats.total_bytes << std::endl;
}
};
// 初始化静态成员
AllocStats Widget::stats;
AlignedMemoryPool Widget::pool(
sizeof(Widget) + alignof(Widget), // 内存块大小(考虑对齐填充)
10, // 10个内存块
std::align_val_t(32) // 32字节对齐
);
int main() {
std::vector<Widget*> widgets;
// 创建5个Widget对象
for (int i = 0; i < 5; ++i) {
try {
Widget* w = new Widget(i);
widgets.push_back(w);
} catch (const std::bad_alloc& e) {
std::cerr << "分配失败: " << e.what() << std::endl;
break;
}
}
Widget::printStats(); // 打印分配统计
// 释放对象
for (auto w : widgets) {
delete w;
}
Widget::printStats(); // 再次打印(应恢复为0)
return 0;
}①全局统计结构AllocStats:记录类的分配次数和总内存大小。
②对齐内存池AlignedMemoryPool:使用std::aligned_alloc分配对齐内存(支持 32 字节对齐)。
③Widget类的重载方法:
operator new从内存池分配内存,并更新统计信息。
operator delete将内存归还内存池,并更新统计信息。
运行结果:
Widget分配统计:
总分配次数: 5
总分配字节数: 400 // 假设每个Widget占80字节(含对齐填充),5*80=400
Widget分配统计:
总分配次数: 0
总分配字节数: 0new的协同使用在某些情况下,需要结合类特定new和定位 new:
new负责分配内存(可能来自内存池)。#include <iostream>
#include <new>
class HeavyObject {
private:
int* data; // 大数组模拟"重"资源
public:
HeavyObject() {
data = new int[1000000]; // 模拟大量内存分配
std::cout << "HeavyObject 构造完成" << std::endl;
}
~HeavyObject() {
delete[] data;
std::cout << "HeavyObject 析构完成" << std::endl;
}
void work() const {
std::cout << "HeavyObject 工作中..." << std::endl;
}
// 类特定new:普通版本(分配内存)
static void* operator new(size_t size) {
std::cout << "类特定new:分配 " << size << " 字节" << std::endl;
return ::operator new(size); // 调用全局new分配内存
}
// 类特定delete:普通版本(释放内存)
static void operator delete(void* p, size_t size) {
std::cout << "类特定delete:释放 " << size << " 字节" << std::endl;
::operator delete(p); // 调用全局delete释放内存
}
// 定位new专用重载:接受void*参数
static void* operator new(size_t size, void* ptr) {
std::cout << "定位new:在地址 " << ptr << " 构造对象" << std::endl;
return ptr; // 直接返回传入的内存地址
}
// 定位new配套的delete(用于构造失败时的回滚)
static void operator delete(void* ptr, void*) noexcept {
std::cout << "定位delete:无需释放内存(由用户管理)" << std::endl;
// 定位new的delete不执行实际释放,因为内存由用户管理
}
};
int main() {
// 1. 分配原始内存(调用类特定new)
void* mem = HeavyObject::operator new(sizeof(HeavyObject));
// 2. 使用定位new构造对象
HeavyObject* obj = new (mem) HeavyObject();
// 3. 使用对象
obj->work();
// 4. 显式调用析构函数(定位new需要手动析构)
obj->~HeavyObject();
// 5. 释放原始内存(调用类特定delete)
HeavyObject::operator delete(mem, sizeof(HeavyObject));
return 0;
}关键流程:
operator new分配原始内存(不构造对象)。operator delete释放原始内存。运行结果:

new/delete的设计原则std::bad_alloc,释放空指针应安全(不执行操作)。operator delete正确释放operator new分配的内存(尤其是自定义内存池时)。alignas(16)),需重载对齐版本的operator new/delete。malloc调用次数)。new让相关对象分配到连续内存(提升 CPU 缓存命中率)。operator new[]分配连续内存(而非多个operator new)。 技术 | 核心功能 | 典型场景 | 关键注意事项 |
|---|---|---|---|
定位 new | 在已分配内存上构造对象 | 内存池、固定地址对象、延迟构造 | 手动调用析构函数 |
类特定new/delete | 自定义类的内存分配逻辑 | 性能优化、内存对齐、分配统计 | 静态成员、异常安全、对齐支持 |
通过灵活运用定位 new 和类特定new/delete,可以显著提升 C++ 程序的内存管理效率,满足高性能、低延迟或特定硬件的需求。在实际项目中,建议结合内存池、对齐分配等技术,根据具体场景选择合适的内存管理策略。