为什么必须为存储在堆中的函数的局部变量调用 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?
假设您有以下功能:
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?