C++内存管理

有关C++内存管理问题总结如下

基础问题

C++中堆和栈的区别是什么

  1. 栈由系统分配释放,栈上变量的生命周期是确定的,一般与作用域有关。栈的地址由高到低,栈上分配的空间大小在编译时通常已知。
  2. 堆由程序员手动开辟释放,堆上的变量除非被显示释放,否则会持续存在。堆的地址由低到高,堆上几乎可以分配任意大小的内存块,但可能会造成内存碎片。
  3. 这里说一下C++的内存分区:堆区、栈区、data区、bss段、代码段。数据data区存放的是静态变量和初始化的全局变量,bss段存放的是未初始化的全局变量。

什么是RAII?为什么它在C++中很重要

RAII是一种编程思想和设计模式,核心思想是:将资源的获取与对象的初始化捆绑在一起,将资源的释放与对象的销毁捆绑在一起。这样,资源管理就与对象的生命周期紧密关联。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
#include <iostream>
#include <fstream>

class File {
private:
std::fstream fs;

public:
File(const std::string& filename) {
fs.open(filename, std::ios::in | std::ios::out);
if (!fs.is_open()) {
throw std::runtime_error("Failed to open the file");
}
}

// 其他与文件相关的操作...

~File() {
if (fs.is_open()) {
fs.close();
}
}
};

int main() {
try {
File myFile("sample.txt");
// 进行文件操作...

} catch (const std::exception& e) {
std::cerr << "Error: " << e.what() << std::endl;
}

// 当myFile对象离开其作用域时,它的析构函数会自动被调用,从而关闭文件
}

解释newdelete,与mallocfree的区别

  • 属性的区别

    new/delete:这两个是C++中的关键字;

    malloc/free:这两个是库函数;

  • 使用上的区别

    malloc:申请空间需要显式填入申请内存的大小;

    new:无需显式填入申请内存的大小,new会根据new的类型分配内存;

  • 返回类型的区别

    new操作符内存分配成功,返回的是对象类型的指针,类型严格与对象匹配,无需进行类型转换,故new是符合类型安全性的操作符。

    malloc内存分配成功返回的是void*指针,需要通过强制类型转换,转换成我们需要的类型。

    所以C++newmalloc安全可靠。

  • 分配失败的区别

    malloc分配失败会返回NULL,我们可以通过判断返回值是否是NULL得知是否分配成功。

    new分配失败会抛出bad_alloc异常。

  • 扩张内存的区别

    malloc有内存扩张机制(通过realloc实现)。

    new没有扩张内存机制。

中级问题

为什么C++推荐使用智能指针,如shared_ptrunique_ptr

  • 自动管理内存:对于unique_ptr,当它超出作用域或者被重新分配时,它指向的对象会被删除。对于shared_ptr,当它的引用计数为0时,它指向的对象会被删除。
  • 异常安全:当函数抛出异常,智能指针确保资源被正确清理,避免资源泄露。
  • 防止悬挂指针:悬挂指针是指指向已经释放内存的指针。unique_ptrshared_ptr可以减少悬挂指针的风险,因为他们确保在没有引用的时候释放资源。

你能解释shared_ptr中的引用计数机制是如何工作的吗

  • 通过一个指针实现引用计数功能,加锁,保证线程安全

什么情况下会导致内存泄漏,你如何检测和预防

  • 指针重新赋值

    1
    2
    3
    4
    int *p = new int();
    int *np = new int();
    p = np;
    //p原来的指向的内存无法释放,因为现在没有指针指向这块内存
  • 错误的内存释放

    假设有一个指针p指向10字节的内存,该内存的第三个字节np又指向某个动态分配的内存, 如果此时你直接delete(p),则会导致np指向的内存无法释放。

  • 返回值的不正确处理

    1
    2
    3
    4
    5
    6
    7
    8
    9
    int *f(){
    return new int(42);
    }

    void f1(){
    f();
    }

    //由于没有对函数f()的返回值做正确接收,将会导致f函数分配的内存无法释放。

关于内存泄露可以使用工具:Valgrind

高级问题

描述C++的内存模型是什么?如何保证线程间的数据同步?

什么是内存屏障(memory barrier)或内存栅栏?在哪些场景中需要使用它?

你有没有用过定制的内存分配器,比如为某些高性能的应用场景?如果有,请描述其工作原理和使用场景。

实践问题

描述一个你曾遇到的复杂的内存相关bug,你是如何诊断和解决的?

你如何评估一个C++程序的内存使用效率?你使用过哪些工具或技术

请编写一个小程序,其中创建一个有资源泄漏的类,然后展示如何使用工具或方法找到并修复该泄漏。