我是 C++ 的新手,所以这可能是一个初学者问题。它考虑了做一些我怀疑相当普遍的事情的“适当”风格。
我正在编写一个函数,该函数在履行其职责时会在堆上分配内存以供调用者使用。我很好奇这个函数的好的原型(prototype)应该是什么样子。现在我有:
int f(char** buffer);
要使用它,我会写:
char* data;
int data_length = f(&data);
// ...
delete[] data;
但是,我将一个指针传递给一个指针这一事实提示我,我可能以错误的方式执行此操作。
有没有人愿意开导我?
最佳答案
在 C 中,这或多或少是合法的。
在 C++ 中,函数通常不应该这样做。你应该尝试使用 RAII以保证内存不会泄漏。
现在您可能会说“它怎么会泄漏内存,我就在那里调用 delete[]
!”,但是如果 // ...
抛出异常怎么办?线条?
根据函数的确切用途,您可以考虑多种选择。一个明显的方法是用 vector 替换数组:
std::vector<char> f();
std::vector<char> data = f();
int data_length = data.size();
// ...
//delete[] data;
现在我们不再需要显式删除,因为 vector 是在堆栈上分配的,当它超出范围时会调用它的析构函数。
我应该提一下,作为对评论的回应,上面的内容暗示了 vector 的拷贝,这可能会很昂贵。大多数编译器会,如果 f
功能不是太复杂,优化那个拷贝,这样就可以了。 (如果函数调用不频繁,开销将不会很重要无论如何)。但如果这没有发生,您可以将一个空数组传递给 f
通过引用函数,并且有f
将其数据存储在其中,而不是返回一个新 vector 。
如果返回拷贝的性能 Not Acceptable ,另一种选择是完全解耦容器的选择,并改用迭代器:
// definition of f
template <typename iter>
void f(iter out);
// use of f
std::vector<char> vec;
f(std::back_inserter(vec));
现在可以使用通常的迭代器操作(*out
引用或写入当前元素,++out
将迭代器向前移动到下一个元素)——更重要的是,所有标准算法现在都将工作。你可以使用 std::copy
例如,将数据复制到迭代器。这是标准库通常选择的方法(即,这是一个好主意;))当函数必须返回数据序列时。
另一种选择是让您自己的对象负责分配/解除分配:
struct f { // simplified for the sake of example. In the real world, it should be given a proper copy constructor + assignment operator, or they should be made inaccessible to avoid copying the object
f(){
// do whatever the f function was originally meant to do here
size = ???
data = new char[size];
}
~f() { delete[] data; }
int size;
char* data;
};
f data;
int data_length = data.size;
// ...
//delete[] data;
同样,我们不再需要显式删除,因为分配是由堆栈上的对象管理的。后者显然需要更多的工作,并且有更多的错误空间,所以如果标准 vector 类(或其他标准库组件)可以完成这项工作,则更喜欢它们。此示例仅在您需要根据您的情况进行定制的情况下使用。
C++ 中的一般经验法则是“如果您在 RAII 对象之外编写 delete
或 delete[]
,那么您就错了。如果您正在编写 new
或 `new [] 在 RAII 对象之外,你做错了,除非结果立即传递给智能指针”
关于c++ - 在堆上分配内存的函数原型(prototype) (C/C++),我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/1171923/