当前位置: 首页 > ops >正文

More Effective C++ 条款08:理解各种不同意义的new和delete

More Effective C++ 条款08:理解各种不同意义的new和delete


核心思想C++中的"new"和"delete"有多种不同含义,包括new操作符、operator new函数、placement new以及对应的delete形式。理解这些不同形式的区别和适用场景对于正确管理内存和避免资源泄漏至关重要。

🚀 1. 问题本质分析

1.1 new的多种含义

  • new操作符(new operator):语言内置的操作符,完成内存分配和对象构造
  • operator new函数:只分配内存,不调用构造函数
  • placement new:在已分配的内存上构造对象

1.2 delete的对应形式

  • delete操作符(delete operator):调用析构函数并释放内存
  • operator delete函数:只释放内存,不调用析构函数
  • placement delete:与placement new对应,只调用析构函数不释放内存
// 不同形式的new和delete示例
class MyClass {
public:MyClass() { std::cout << "Constructor\n"; }~MyClass() { std::cout << "Destructor\n"; }// 自定义operator newstatic void* operator new(size_t size) {std::cout << "Custom operator new, size: " << size << "\n";return ::operator new(size);}// 自定义operator deletestatic void operator delete(void* ptr) {std::cout << "Custom operator delete\n";::operator delete(ptr);}
};// 使用示例
void demonstrateNews() {// 1. new操作符:分配内存并构造对象MyClass* obj1 = new MyClass;  // 调用自定义operator new,然后构造函数// 2. operator new函数:只分配内存void* rawMemory = MyClass::operator new(sizeof(MyClass));  // 只分配,不构造// 3. placement new:在已分配内存上构造对象MyClass* obj2 = new(rawMemory) MyClass;  // 在rawMemory上构造对象// 对应的删除操作delete obj1;                    // 调用析构函数并释放内存obj2->~MyClass();               // 只调用析构函数(placement delete的替代)MyClass::operator delete(rawMemory);  // 只释放内存
}

📦 2. 问题深度解析

2.1 各种new/delete形式的对比

形式功能是否调用构造函数/析构函数是否分配/释放内存
new操作符分配内存并构造对象
operator new只分配内存
placement new在指定内存位置构造对象
delete操作符调用析构函数并释放内存
operator delete只释放内存
placement delete调用析构函数但不释放内存

2.2 常见误用和问题

// ❌ 错误的使用示例
class Problematic {
public:Problematic() { buffer = new char[100]; }~Problematic() { delete[] buffer; }private:char* buffer;
};void demonstrateProblems() {// 1. 错误:混用new操作符和operator deleteProblematic* obj = new Problematic;// ::operator delete(obj);  // ❌ 错误:不会调用析构函数,导致buffer泄漏// 2. 错误:混用operator new和delete操作符void* memory = ::operator new(sizeof(Problematic));Problematic* obj2 = static_cast<Problematic*>(memory);// delete obj2;  // ❌ 错误:对未构造的对象调用析构函数,未定义行为// 3. 错误:错误的placement delete使用Problematic* obj3 = new(memory) Problematic;// ::operator delete(obj3);  // ❌ 错误:不会调用析构函数,导致buffer泄漏obj3->~Problematic();  // ✅ 正确:先调用析构函数::operator delete(memory);  // ✅ 正确:再释放内存
}

⚖️ 3. 解决方案与最佳实践

3.1 正确使用模式

// ✅ 正确的内存管理模式
class CorrectUsage {
public:CorrectUsage() { data = new int[100]; }~CorrectUsage() { delete[] data; }// 自定义operator new(可选)static void* operator new(size_t size) {std::cout << "Allocating " << size << " bytes\n";return ::operator new(size);}// 自定义operator delete(可选)static void operator delete(void* ptr) {std::cout << "Freeing memory\n";::operator delete(ptr);}// 自定义placement new(高级用法)static void* operator new(size_t size, void* location) {return location;  // 简单地返回指定位置}// 对应的placement delete(异常安全所需)static void operator delete(void* ptr, void* location) {// 通常为空实现,只在构造函数抛出异常时被调用}private:int* data;
};void demonstrateCorrectUsage() {// 1. 正常使用new/deleteCorrectUsage* obj1 = new CorrectUsage;delete obj1;// 2. 使用operator new/delete进行低级内存管理void* rawMem = CorrectUsage::operator new(sizeof(CorrectUsage));CorrectUsage* obj2 = new(rawMem) CorrectUsage;  // placement newobj2->~CorrectUsage();  // 显式调用析构函数CorrectUsage::operator delete(rawMem);  // 释放内存// 3. 数组版本的new/deleteCorrectUsage* array = new CorrectUsage[5];delete[] array;
}

3.2 高级应用:自定义内存管理

// 内存池实现示例
class MemoryPool {
public:MemoryPool(size_t objectSize, size_t chunkSize = 100): objectSize_(objectSize), chunkSize_(chunkSize) {allocateChunk();}~MemoryPool() {for (void* chunk : chunks_) {::operator delete(chunk);}}void* allocate() {if (freeList_ == nullptr) {allocateChunk();}void* block = freeList_;freeList_ = *static_cast<void**>(freeList_);return block;}void deallocate(void* block) {*static_cast<void**>(block) = freeList_;freeList_ = block;}private:void allocateChunk() {void* chunk = ::operator new(chunkSize_ * objectSize_);chunks_.push_back(chunk);// 构建空闲链表char* p = static_cast<char*>(chunk);for (size_t i = 0; i < chunkSize_ - 1; ++i) {*reinterpret_cast<void**>(p) = p + objectSize_;p += objectSize_;}*reinterpret_cast<void**>(p) = freeList_;freeList_ = chunk;}size_t objectSize_;size_t chunkSize_;void* freeList_ = nullptr;std::vector<void*> chunks_;
};// 使用自定义内存池的类
class PooledObject {
public:static void* operator new(size_t size) {if (size != sizeof(PooledObject)) {return ::operator new(size);}return pool_.allocate();}static void operator delete(void* ptr) {if (ptr == nullptr) return;pool_.deallocate(ptr);}private:static MemoryPool pool_;int data[100];
};MemoryPool PooledObject::pool_(sizeof(PooledObject));

3.3 现代C++增强

// 使用alignas和aligned_alloc(C++17)
class AlignedObject {
public:AlignedObject() = default;static void* operator new(size_t size) {// 要求64字节对齐constexpr size_t alignment = 64;if (size != sizeof(AlignedObject)) {return ::operator new(size);}#ifdef _WIN32return _aligned_malloc(size, alignment);#elsereturn aligned_alloc(alignment, size);#endif}static void operator delete(void* ptr) {if (ptr == nullptr) return;#ifdef _WIN32_aligned_free(ptr);#elsefree(ptr);#endif}private:alignas(64) int data[100];  // 确保对象本身也是64字节对齐
};// 使用std::pmr::memory_resource(C++17)
#include <memory_resource>
class PmrObject {
public:explicit PmrObject(std::pmr::memory_resource* mr = nullptr) {if (mr == nullptr) {mr = std::pmr::get_default_resource();}data = mr->allocate(100 * sizeof(int));}~PmrObject() {auto mr = std::pmr::get_default_resource();mr->deallocate(data, 100 * sizeof(int));}// 使用placement new和自定义删除器static void* operator new(size_t size, std::pmr::memory_resource* mr) {return mr->allocate(size);}static void operator delete(void* ptr, std::pmr::memory_resource* mr) {mr->deallocate(ptr, sizeof(PmrObject));}// 常规的operator deletestatic void operator delete(void* ptr) {auto mr = std::pmr::get_default_resource();mr->deallocate(ptr, sizeof(PmrObject));}private:void* data;
};

💡 关键实践原则

  1. 区分new操作符和operator new
    理解它们的不同职责:

    // new操作符:分配内存+构造对象
    MyClass* obj = new MyClass;// operator new:只分配内存
    void* memory = MyClass::operator new(sizeof(MyClass));
    
  2. 配对使用new/delete形式
    确保分配和释放方式匹配:

    // 匹配的new/delete对
    MyClass* obj = new MyClass;      // 使用new操作符
    delete obj;                      // 使用delete操作符void* mem = ::operator new(100); // 使用operator new
    ::operator delete(mem);          // 使用operator deleteMyClass* p = new(buffer) MyClass; // 使用placement new
    p->~MyClass();                   // 手动调用析构函数(placement delete的替代)
    
  3. 为placement new提供对应的placement delete
    确保异常安全:

    class ExceptionSafe {
    public:// placement newstatic void* operator new(size_t size, void* location) {return location;}// 对应的placement delete(在构造函数抛出异常时调用)static void operator delete(void* ptr, void* location) {// 清理资源,但不释放内存(因为内存不是我们分配的)}
    };
    
  4. 重载operator new/delete要小心
    遵循惯例和规范:

    class CustomAllocator {
    public:// 常规operator newstatic void* operator new(size_t size) {if (size != sizeof(CustomAllocator)) {return ::operator new(size);  // 委托给全局版本}return customAllocate(size);}// 对应的operator deletestatic void operator delete(void* ptr) {if (ptr == nullptr) return;customDeallocate(ptr);}// 数组版本static void* operator new[](size_t size) {return customAllocate(size);}static void operator delete[](void* ptr) {if (ptr == nullptr) return;customDeallocate(ptr);}
    };
    

现代C++增强

// 使用noexcept规范
class NoExceptAllocator {
public:static void* operator new(size_t size) noexcept {try {return ::operator new(size);} catch (...) {return nullptr;  // 返回nullptr而不是抛出异常}}static void operator delete(void* ptr) noexcept {::operator delete(ptr);}
};// 使用constexpr分配(C++20)
struct ConstexprAllocator {constexpr static void* operator new(size_t size) {// 在编译时分配内存(如果支持)// 注意:这需要编译器支持return nullptr; // 伪实现}constexpr static void operator delete(void* ptr) {// 编译时释放}
};// 使用分配器感知设计
template<typename Allocator = std::allocator<MyType>>
class AllocatorAware {
public:using allocator_type = Allocator;explicit AllocatorAware(const Allocator& alloc = Allocator()): allocator_(alloc) {}// 使用分配器分配内存void* allocate(size_t n) {return allocator_.allocate(n);}void deallocate(void* p, size_t n) {allocator_.deallocate(static_cast<value_type*>(p), n);}private:Allocator allocator_;
};

代码审查要点

  1. 检查new/delete是否正确配对使用
  2. 确认自定义operator new/delete是否正确处理大小和边界情况
  3. 验证placement new是否有对应的placement delete用于异常安全
  4. 检查内存分配是否考虑了对齐要求
  5. 确认资源管理类是否正确实现所有必要的new/delete形式

总结
C++提供了多种形式的new和delete,每种都有特定的用途和语义。new操作符负责分配内存和构造对象,operator new只分配内存,placement new在指定位置构造对象。对应的delete形式需要正确配对使用以确保资源正确释放。理解这些不同形式的区别对于实现高效、安全的内存管理至关重要。在现代C++中,可以利用内存池、自定义分配器、对齐分配等高级技术来优化内存使用,同时需要确保异常安全和资源管理的正确性。正确使用各种new和delete形式是编写高质量C++代码的基础。

http://www.xdnf.cn/news/18647.html

相关文章:

  • (操作系统)死锁是什么 必要条件 解决方式
  • 【Task05】:向量数据库实践(第三章3、4节)
  • Fory序列化与反序列化
  • ArcGIS JSAPI 高级教程 - 创建渐变色材质的自定义几何体
  • MYSQL(DDL)
  • 算法:驱动智能社会的核心引擎
  • OpenIM应用机器人自动应答
  • Zabbix 7.0中文乱码矫正
  • AI产品经理面试宝典第75天:Agentic RAG系统优化策略面试题实战解析
  • 08-系统能力调用与权限管理
  • BERT(Bidirectional Encoder Representations from Transformers)模型详解
  • 【RAGFlow代码详解-1】概述
  • 【Android】从一个AndroidRuntime看类的加载
  • 结构化智能编程:用树形向量存储重构AI代码理解范式
  • 第16届蓝桥杯C++中高级选拔赛(STEMA)2025年4月真题
  • More Effective C++ 条款05: 谨慎定义类型转换函数
  • 【Flex SerialPort】一个基于Qt6的支持自定义按键指令的串口工具
  • Kubernetes保姆级教学
  • centos搭建gitlab服务器
  • 【贪心算法】day2
  • 邮箱创建时间打标与自动删除功能设计思路
  • 13种常见机器学习算法面试总结(含问题与优质回答)
  • MySQL视图有什么用?一文读懂虚拟表的六大核心价值
  • String的最大长度剖析
  • 港口集装箱编号识别误识率↓79%!陌讯多模态融合算法落地优化
  • docker 镜像问题(解决了)
  • 第二重境:视角切换——用心灵的望远镜,看见问题的全局
  • 基于 Redis + JWT 的跨系统身份共享方案
  • Vue2+Vue3前端开发笔记合集
  • 【运维进阶】case、for、while、until语句大合集