C++

C++ 知识量:19 - 82 - 316

19.1 控制内存分配><

重载new和delete- 19.1.1 -

在C++中,可以重载new和delete运算符以改变它们的行为。这通常在自定义内存管理时非常有用。

下面是一个简单的示例,说明了如何重载全局的new和delete运算符:

#include <cstdlib>  
#include <iostream>  
  
class MyClass {  
public:  
    static void* operator new(size_t size) {  
        std::cout << "Overloaded new operator.\n";  
        void* ptr = std::malloc(size);  
        return ptr;  
    }  
      
    static void operator delete(void* ptr) {  
        std::cout << "Overloaded delete operator.\n";  
        std::free(ptr);  
    }  
};  
  
int main() {  
    MyClass* ptr = new MyClass(); // 将会调用重载的new运算符  
    delete ptr; // 将会调用重载的delete运算符  
    return 0;  
}

在这个示例中,使用new创建MyClass的实例时,会调用重载的new运算符,它打印一条消息,并使用std::malloc分配内存。同样,使用delete删除这个实例时,会调用重载的delete运算符,它打印一条消息,并使用std::free释放内存。

请注意,重载的new和delete运算符必须是静态的,因为它们在对象创建和销毁之前执行。它们也必须是void*类型,因为它们需要接受一个指向内存块的指针。

另外,重载全局的new和delete运算符应当谨慎使用,因为它们会影响所有使用这些运算符的代码。在大多数情况下,可能会更希望重载类的成员函数版本的new和delete运算符,这样就可以更精确地控制哪些代码使用了自定义内存管理。

malloc函数与free函数- 19.1.2 -

malloc()和free()是C语言的标准库函数,用于动态内存分配和释放。在C++中,也可以使用它们来进行动态内存管理。

malloc()函数根据指定的字节数来分配内存。它的函数原型如下:

void* malloc(size_t size);

其中,size是要分配的字节数。malloc()函数返回一个指向分配的内存块的指针,如果内存分配成功,否则返回NULL。

下面是一个使用malloc()函数的例子:

#include <iostream>  
#include <cstdlib>  
  
int main() {  
    int* ptr = (int*)malloc(10 * sizeof(int)); // 分配10个整数的内存空间  
    if (ptr == NULL) {  
        std::cerr << "Memory allocation failed!" << std::endl;  
        return 1;  
    }  
  
    for (int i = 0; i < 10; ++i) {  
        ptr[i] = i; // 将分配的内存空间初始化为一些值  
    }  
  
    for (int i = 0; i < 10; ++i) {  
        std::cout << ptr[i] << " "; // 打印内存中的值  
    }  
    std::cout << std::endl;  
  
    free(ptr); // 释放内存空间  
    return 0;  
}

在上面的例子中,使用malloc()函数分配了10个整数的内存空间,并将其存储在一个整型指针ptr中。然后,使用循环将分配的内存空间初始化为一些值,并打印出来。最后,使用free()函数释放了分配的内存空间。

free()函数的原型如下:

void free(void* ptr);

其中,ptr是要释放的内存块的指针。free()函数释放指定的内存块,使其可以被重新分配。注意,不要尝试访问已经释放的内存块,因为这可能会导致未定义的行为。

定位new表达式- 19.1.3 -

定位new(placement new)是C++中的一个特殊语法,它允许程序员在已经分配的内存中直接构造对象,而不是通过常规的new操作符来分配内存并构造对象。这提供了一种将对象的内存分配和初始化分离的机制,从而可以更精细地控制对象的创建和销毁过程。

定位new的一般语法形式如下:

new (place_address) type(initializer_list);

其中,place_address是一个指针,指向已经分配好的内存区域的起始地址。type是要构造的对象的类型,initializer_list是对象的初始化参数列表。

使用定位new时,需要注意以下几点:

  1. 内存分配和对象构造分离:定位new假设内存已经通过某种方式分配好了,它只负责在指定的内存位置构造对象。因此,在使用定位new之前,必须确保所提供的内存地址是有效的,并且足够大以容纳要构造的对象。

  2. 不自动释放内存:与常规的new操作符不同,定位new不会自动释放对象所占用的内存。在对象生命周期结束时,需要显式调用对象的析构函数来销毁对象,并手动释放内存。

  3. 适用于自定义内存管理:定位new特别适用于需要自定义内存管理的场景,例如使用自定义的内存分配器或在嵌入式系统中管理受限的内存资源。通过定位new,可以将对象的构造过程与内存分配策略解耦,从而实现更灵活和高效的内存管理。

下面是一个简单的示例,演示了如何使用定位new在预先分配的内存中构造对象:

#include <iostream>  
#include <new> // 包含定位 new 的声明  
  
class MyClass {  
public:  
    int value;  
      
    MyClass(int v) : value(v) {  
        std::cout << "MyClass constructed with value: " << value << std::endl;  
    }  
      
    ~MyClass() {  
        std::cout << "MyClass destroyed with value: " << value << std::endl;  
    }  
};  
  
int main() {  
    char memory[sizeof(MyClass)]; // 预先分配足够的内存来容纳 MyClass 对象  
    MyClass* obj = new (memory) MyClass(42); // 使用定位 new 在指定的内存中构造 MyClass 对象  
    // ... 使用 obj ...  
    obj->~MyClass(); // 显式调用析构函数销毁对象  
    // 注意:这里没有使用 delete 释放内存,因为内存是预先分配的,不是通过 new 分配的。  
    return 0;  
}