在 C 中,这或多或少是合法的。
在 C++ 中,函数通常不应该这样做。您应该尝试使用RAII来保证内存不会泄漏。
现在你可能会说“它怎么会泄漏内存,我delete[]
就在那里调用!”,但是如果在行中抛出异常// ...
怎么办?
根据功能的确切用途,您有几个选项需要考虑。一个明显的方法是用向量替换数组:
std::vector<char> f();
std::vector<char> data = f();
int data_length = data.size();
// ...
//delete[] data;
现在我们不再需要显式删除,因为向量是在堆栈上分配的,当它超出范围时会调用它的析构函数。
作为对评论的回应,我应该提到,上述内容暗示了向量的副本,这可能会很昂贵。f
如果函数不太复杂,大多数编译器会优化该副本,这样就可以了。(如果该函数不经常被调用,那么开销无论如何都无关紧要)。但如果这没有发生,您可以改为通过f
引用将一个空数组传递给函数,f
并将其数据存储在其中,而不是返回一个新向量。
如果返回副本的性能不可接受,另一种选择是完全分离容器的选择,并使用迭代器:
// 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;
同样,我们不再需要显式删除,因为分配是由堆栈上的对象管理的。后者显然工作量更大,出错的空间也更大,所以如果标准向量类(或其他标准库组件)能胜任,更喜欢它们。此示例仅适用于您需要根据您的情况定制的内容。
C++ 中的一般经验法则是“如果你在 RAII 对象之外编写一个delete
or delete[]
,那么你做错了。如果你在new
RAII 对象之外编写一个 or `new[],你就是在这样做错误,除非结果立即传递给智能指针”