C++中的内存分配器:自定义new/delete的操作

讲座主题:C++中的内存分配器——自定义new/delete的操作

大家好!欢迎来到今天的讲座。今天我们要聊聊一个既神秘又有趣的主题:C++中的内存分配器,以及如何自定义newdelete操作符。听起来很复杂?别担心,我会用轻松幽默的语言和通俗易懂的例子带你一步步深入了解。

为什么我们需要自定义内存分配?

在C++中,默认的newdelete已经为我们提供了方便的动态内存管理功能。但有时候,我们可能需要更精细地控制内存分配,比如:

  1. 性能优化:默认的内存分配器可能不够高效,特别是在频繁分配和释放小块内存的情况下。
  2. 调试用途:通过自定义内存分配器,我们可以检测内存泄漏或越界访问等问题。
  3. 特殊需求:某些嵌入式系统可能需要特定的内存管理策略。

接下来,我们就来看看如何实现这些目标。


自定义new和delete的基础知识

在C++中,newdelete实际上是两个独立的操作符。new负责分配内存并调用构造函数,而delete则负责调用析构函数并释放内存。

默认行为

先来看一下默认的newdelete是如何工作的:

class MyClass {
public:
    MyClass() { std::cout << "Constructor calledn"; }
    ~MyClass() { std::cout << "Destructor calledn"; }
};

int main() {
    MyClass* obj = new MyClass(); // 分配内存并调用构造函数
    delete obj;                  // 调用析构函数并释放内存
}

输出:

Constructor called
Destructor called

自定义new和delete

现在,让我们看看如何自定义newdelete操作符。

重载全局new和delete

如果你想为整个程序提供一个统一的内存分配策略,可以重载全局的newdelete操作符。

#include <iostream>
#include <cstdlib>

void* operator new(std::size_t size) {
    std::cout << "Custom new called, allocating " << size << " bytesn";
    void* ptr = std::malloc(size);
    if (!ptr) {
        throw std::bad_alloc();
    }
    return ptr;
}

void operator delete(void* ptr) noexcept {
    std::cout << "Custom delete calledn";
    std::free(ptr);
}

int main() {
    int* p = new int(42); // 使用自定义new
    delete p;             // 使用自定义delete
}

输出:

Custom new called, allocating 4 bytes
Custom delete called

小贴士:注意operator delete的签名必须包含noexcept,否则会导致编译错误。


类级别的new和delete

如果你只想为某个类提供特殊的内存管理策略,可以在类内部重载newdelete

class MyClass {
public:
    void* operator new(std::size_t size) {
        std::cout << "MyClass custom new called, allocating " << size << " bytesn";
        return std::malloc(size);
    }

    void operator delete(void* ptr) noexcept {
        std::cout << "MyClass custom delete calledn";
        std::free(ptr);
    }
};

int main() {
    MyClass* obj = new MyClass(); // 使用MyClass的自定义new
    delete obj;                   // 使用MyClass的自定义delete
}

输出:

MyClass custom new called, allocating 1 bytes
MyClass custom delete called

阵容强大的placement new

除了普通的new,C++还提供了一种叫做placement new的特殊形式。它允许我们在指定的内存地址上创建对象。

#include <new> // 必须包含这个头文件

class MyClass {
public:
    MyClass() { std::cout << "Constructor calledn"; }
    ~MyClass() { std::cout << "Destructor calledn"; }
};

int main() {
    char buffer[sizeof(MyClass)]; // 一块未初始化的内存
    MyClass* obj = new (buffer) MyClass(); // 在buffer上构造对象
    obj->~MyClass();                      // 手动调用析构函数
}

输出:

Constructor called
Destructor called

警告:使用placement new时,记得手动调用析构函数,否则可能导致资源泄漏。


表格总结:不同new的比较

类型 描述 使用场景
普通new 动态分配内存并调用构造函数 通用动态内存分配
全局new 为整个程序提供统一的内存分配策略 性能优化、调试
类级别new 为特定类提供定制化的内存管理策略 嵌入式系统、特殊需求
placement new 在指定内存地址上构造对象 对象池、内存复用

国外技术文档引用

  • ISO C++ Standard:描述了newdelete的基本语义,并规定了它们的行为。
  • Scott Meyers《Effective C++》:深入探讨了自定义内存分配器的设计原则和最佳实践。
  • Bjarne Stroustrup《The C++ Programming Language》:详细介绍了C++中内存管理的底层机制。

结语

今天我们聊了C++中自定义newdelete的操作。从全局到类级别,再到placement new,每一种方式都有其独特的应用场景。希望这篇文章能帮助你更好地理解和掌握C++的内存管理技巧。

下一次讲座,我们将深入探讨C++中的智能指针。敬请期待!

谢谢大家!

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注