MST

星途 面试题库

面试题:C++下malloc和new内存分配在底层实现及对象构造析构上的深度剖析

深入探讨C++中malloc和new在底层内存分配机制上的差异,例如它们如何与操作系统的内存管理交互。同时,结合对象的构造和析构函数,说明new是如何确保对象在分配内存后正确构造,而malloc分配的内存用于对象存储时,开发者需要额外注意哪些问题?
44.5万 热度难度
编程语言C++

知识考点

AI 面试

面试题答案

一键面试

1. malloc和new在底层内存分配机制上的差异

  • 内存分配方式
    • mallocmalloc是C标准库函数,它从堆(heap)上分配内存。其基本工作原理是向操作系统请求一块指定大小的连续内存空间。操作系统维护一个记录空闲内存块的链表,malloc遍历该链表,寻找一个大小足够的空闲块。如果找到,就将该块标记为已使用,并返回指向该块起始地址的指针。如果找不到合适大小的块,可能会尝试扩展堆空间(通过系统调用,如brkmmap,具体取决于操作系统)。
    • newnew是C++的关键字,它不仅分配内存,还会调用对象的构造函数。在底层,new首先调用operator new函数(通常可以重载)来分配内存,operator new本质上也是通过调用malloc或类似的底层内存分配函数来从堆上获取内存。不同之处在于,new在获取内存后会根据对象类型调用相应的构造函数进行初始化。
  • 与操作系统内存管理的交互
    • malloc:直接依赖操作系统提供的内存分配函数,如在Linux下通过brkmmap系统调用与内核交互来获取和管理堆内存。它不了解对象的类型信息,只是单纯地分配指定字节数的内存。
    • newnew在调用operator new获取内存后,会根据对象类型进行额外的处理。operator new调用malloc获取内存,之后C++运行时系统负责调用对象的构造函数。在释放内存时,delete调用operator delete(通常可重载),operator delete再调用free来释放内存,并且在释放前会调用对象的析构函数。

2. new确保对象正确构造

new在分配内存后会自动调用对象的构造函数。例如:

class MyClass {
public:
    MyClass() {
        // 构造函数的初始化代码
        std::cout << "MyClass constructor called" << std::endl;
    }
    ~MyClass() {
        std::cout << "MyClass destructor called" << std::endl;
    }
};

MyClass* obj = new MyClass();

在上述代码中,new MyClass()首先调用operator new分配内存,然后自动调用MyClass的构造函数,确保对象在创建时被正确初始化。

3. malloc分配内存用于对象存储时开发者需注意的问题

  • 手动调用构造函数:使用malloc分配内存后,不会自动调用对象的构造函数。例如:
class MyClass {
public:
    MyClass() {
        std::cout << "MyClass constructor called" << std::endl;
    }
    ~MyClass() {
        std::cout << "MyClass destructor called" << std::endl;
    }
};

MyClass* obj = static_cast<MyClass*>(malloc(sizeof(MyClass)));
if (obj) {
    new (obj) MyClass(); // 定位new,手动调用构造函数
}
  • 手动调用析构函数和释放内存:使用malloc分配的内存,在释放时需要手动调用析构函数,然后再调用free释放内存。例如:
if (obj) {
    obj->~MyClass(); // 手动调用析构函数
    free(obj);
}
  • 类型安全malloc返回的是void*类型指针,需要进行显式类型转换,这可能导致类型安全问题。而new返回的指针类型与对象类型匹配,更安全。

总之,newmalloc在内存分配和对象管理上有明显差异,在C++中使用new能更方便、安全地创建对象,而使用malloc需要开发者手动处理对象的构造和析构等操作。