动态数组超出作用域后,内存是否被释放?

3

我正在函数中动态分配一个数组的内存。我的问题是:一旦函数运行完成,内存是否被释放?

代码:

void f(){
    cv::Mat* arr = new cv::Mat[1];
    ...
}

我正在解决这个问题,不使用堆来实现。 - undefined
2
一般规则:每个new对应一个delete,每个new[]对应一个delete[] - undefined
2
或者更好的是...将所有对new的调用都分配给智能指针,而且不要显式调用delete! ;) - user213313
@BleepBloop 你可以解释一下智能指针是什么吗? - undefined
@Aly 看看我的回复。智能指针(查找unique_ptrshared_ptr)是一种像指针一样的类,但在超出其作用域时还会自动删除它们所管理的内存。 - undefined
这里有一个关于智能指针的相关问题,可能会对你有所帮助:https://dev59.com/-HRC5IYBdhLWcg3wJNmf - user213313
5个回答

6
不,使用new分配的内存在指针超出作用域时不会自动释放。
但是,您可以(并且应该)使用C++11的unique_ptr,它在指针超出作用域时处理内存释放:
void f(){
    std::unique_ptr<cv::Mat[]> arr(new cv::Mat[1]);
    ...
}

C++11还提供了shared_ptr用于你可能想要复制的指针。现代C++应该努力使用这些“智能指针”,因为它们提供更安全的内存管理,几乎不会影响性能。


没有C++11啊,我用apt-get安装了gcc,本以为可以得到最新最好的版本,但是看起来unique_ptr对我来说不存在。 - undefined
2
@Aly,当你调用g++时,请尝试使用g++ -std=c++11g++ -std=c++0x。其中之一应该启用unique_ptr - undefined

4
不是的,你必须调用释放它的函数。
delete[] arr;

但是你应该问自己是否有必要进行动态分配。这将不需要显式的内存管理:

void f(){
    cv::Mat arr[1];
    ...
}

如果您需要一个动态大小的数组,可以使用一个std::vector。向量会在内部进行动态分配,但会负责释放其资源。
void f(){
    std::vector<cv::Mat> arr(n); // contains n cv::Mat objects
    ...
}

然而,在C++标准内,你也不能动态调整数组的大小(至少我知道gcc提供了动态大小数组作为扩展功能)。 - undefined
@slavik262 谢谢。我添加了一个使用 vector 的例子。尽管 OP 的例子使用了编译时常量 1 - undefined

4
每次调用new都需要与某个调用delete相匹配。
在您的情况下,arr本身是具有自动存储期的变量。这意味着arr本身将在超出范围时被销毁。但是,arr指向的东西并不会被销毁,因为它是一个没有自动存储期的变量。 arr本身具有自动存储期的事实可以利用起来,通过将原始指针包装在一个类中,在自动对象被销毁时销毁存储的指针。该对象利用一种称为RAII的惯用语法来实现所谓的“智能指针”。由于这是设计良好的应用程序中非常常见的要求,C++标准库提供了许多智能指针类,您可以使用。在C++03中,您可以使用 std::auto_ptr 在C++11中,auto_ptr已被弃用,取而代之的是几个更好的智能指针,其中包括:std::unique_ptrstd::shared_ptr。一般来说,使用智能指针而不是原始(“愚蠢的”)指针是一个好主意。
如果您最终需要的是由C99支持的动态大小数组,则应该知道C++没有直接支持它们。一些编译器(尤其是GCC)确实提供了对动态大小数组的支持,但这些是特定于编译器的语言扩展。为了在仅使用可移植的符合标准的代码的情况下拥有类似于动态大小数组的东西,为什么不使用std::vector呢?
编辑:给数组赋值:
在您的评论中,您描述了分配给此数组的方式,我认为这意味着类似于以下内容:
int* arr = new int[5];
arr[1] = 1;
arr[4] = 2;
arr[0] = 3;

如果是这种情况,你可以使用vector::operator[]来完成相同的操作。这样做看起来与你上面使用的语法非常相似。唯一真正需要注意的是,由于vector是动态大小的,所以在尝试分配位置为N-1的元素之前,你需要确保vector至少有N个元素。这可以通过多种方式实现。
你可以从一开始就创建带有N个项目的vector,这样每个项目都会被初始化为默认值:
vector<int> arr(5); // creates a vector with 5 elements, all initialized to zero
arr[1] = 1;
arr[4] = 2;
arr[0] = 3;

您可以事后调整向量的大小:

您可以resize向量。

vector<int> arr; // creates an empty vector
arr.resize(5); // ensures the vector has exactly 5 elements
arr[1] = 1;
arr[4] = 2;
arr[0] = 3;

你可以使用各种算法来填充向量中的元素。其中一个例子是fill_n

vector<int> arr; // creates empty vector
fill_n(back_inserter(arr), 5, 0);  // fills the vector with 5 elements, each one has a value of zero
arr[1] = 1;
arr[4] = 2;
arr[0] = 3;

嗨,我没有使用向量,因为我以随机方式分配给数组。例如,如果数组的大小为5,我可能会先设置arr[3],然后是arr[1]等等。当我尝试使用向量时,它只是终止了程序,没有异常 :(. 我似乎没有C++11,请给出一个使用std::auto_ptr的代码示例(我对C++非常陌生)。 - undefined
1
@Aly:如果我理解正确的话,你可以按照你描述的方式对 vector 进行赋值。请查看我的修改。 - undefined

1
不行。数组是在堆上分配的,你必须在它超出作用域之前将其删除:
void f() {

    cv::Mat * arr = new cv::Mat[1];

    // ...

    delete [] arr;

}

新的C++代码应尽量避免显式使用deleteunique_ptrshared_ptr会自动处理它并提供更好的所有权语义。 - undefined

1

不,当然不是。

对于每个new,你需要精确地一个delete

对于每个new[],你需要精确地一个delete[]

由于你没有匹配的delete[],你的程序已经出错了。

(因此,使用C++的成年人方式根本不使用new或指针。这样你就不会遇到这些问题。)


1
因此,成熟使用C++的方式是根本不使用new或指针。这样你就不会遇到那些问题了。什么?你是在暗示永远不要使用堆内存吗? - undefined
@slavik262:当然不是。但我强烈建议不要手动使用所谓的堆!库函数在这方面更好,也更安全。 - undefined
使用new是完全可以的,只要你将其包装在C++11智能指针中——有很多情况下,你需要在使用库之外分配自己的内存。 - undefined
@JohnDibling:仅仅出于异常安全的原因,new调用应该放在一组非常受限制的辅助函数中,例如make_unique - undefined
1
@KerrekSB:暂且不论标准库中是否存在 make_unique 的细微差别,我并不反对。 - undefined

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接