为什么必须为存储在堆中的函数的局部变量调用 delete?

Why do you have to call delete for local variables of a function that are stored in the heap?

假设您有以下功能:

 void doSomething(){
    int *data = new int[100];
 }

为什么会产生内存泄漏?既然我不能在函数外访问这个变量,为什么每次调用这个函数结束时编译器不自己调用delete?

像这样的动态分配的全部意义在于您手动控制分配对象的生命周期。它的需要和适当程度 很多 比许多人似乎认为的要少。事实上,我想不出 new[].

的有效用法

让编译器处理对象的生命周期确实是个好主意。这叫做RAII。在这种特殊情况下,您应该使用 std::vector<int> data(100)。现在,一旦 data 以任何方式超出范围,内存就会自动释放。

Since I can not access this variable outside the function, why doesn't the compiler call delete by itself every time a call to this function ends?

因为您不应该在一开始就不会使用的堆上分配内存。 这就是 C++ 的工作原理。

编辑: 另外,如果编译器会在函数 returns 之后删除指针,那么就会有从函数返回指针的方法。

这里有一个在堆上动态分配的 int 数组,其指针数据指向数组的第一个元素。

由于您使用 new 在堆上显式分配了数组,所以当您超出范围时,不会从内存中删除该数组。当你超出范围时,你的指针将被删除,然后你将无法访问数组 -> 内存泄漏

实际上你可以在你的doSomething()函数之外访问这个变量,你只需要知道这个指针保存的地址。

下图中,矩形是一个内存单元,矩形上面是内存单元地址,矩形里面是内存单元值。

例如,在上图中,如果您知道分配的数组从地址 0x200 开始,那么您可以在函数之外执行以下操作:

int *data = (int *)0x200;
std::cout << data[0];

因此编译器无法为您删除此内存,但请注意编译器警告:

main.cpp: In function ‘void doSomething()’:
main.cpp:3:10: warning: unused variable ‘data’ [-Wunused-variable]
     int *data = new int[100];

当您执行 new 时,OS 会在 RAM 中为您分配内存,因此您需要让 OS 知道,当您不再需要此内存时,执行 delete,所以只有你知道什么时候执行 delete,而不是编译器。

Why will this produce a memory leak?

因为您有责任删除您使用 new 创建的任何内容。

Why doesn't the compiler call delete by itself every time a call to this function ends?

通常,编译器无法判断您是否仍有指向已分配对象的指针。例如:

void doSomething(){
    int *data = new int[100];
    doSomethingElse(data);
}

doSomethingElse是否只是在函数调用时使用了指针(在这种情况下我们还是要删除这里的数组)?它是否存储指针的副本供以后使用(在这种情况下我们还不想删除它)?编译器无法知道;由你来告诉它。与其制定复杂且容易出错的规则(如 "you must delete it unless you can figure out that the compiler must know that there are no other references to it"),不如让规则保持简单:您必须删除它。

幸运的是,我们可以做得比处理原始指针并尝试在正确的时间删除它更好。 RAII 的原则允许对象获得分配资源的所有权,并在它们超出范围时调用析构函数时自动释放它们。容器允许在单个范围内维护动态对象,并在需要时进行复制;智能指针允许所有权在范围之间移动或共享。在这种情况下,一个简单的容器将为我们提供一个动态数组:

void doSomething(){
    std::vector<int> data(100);
} // automatically deallocated

当然,对于像这样的固定大小的小数组,你还不如让它自动:

void doSomething(){
    int data[100];
} // all automatic variables are deallocated

这是在堆上分配的内存,因此 因此在函数外部可用于任何具有其地址的对象。不能指望编译器知道您打算对堆分配的内存做什么,并且它不会在编译时推断需要通过(例如)跟踪是否有任何东西具有对内存的活动引用来释放内存。也没有 Java 中那样的自动 运行 时间垃圾收集。在 C++ 中,程序员有责任释放堆上分配的所有内存。

另请参阅:Why doesn't C++ have a garbage collector?