如何在函数内返回指针值后删除指针

35

我有这个函数:

char* ReadBlock(fstream& stream, int size)
{
    char* memblock;
    memblock = new char[size];
    stream.read(memblock, size);
    return(memblock);
}

每次从文件读取字节时都会调用该函数。我认为每次使用它时都会分配新的内存,但一旦我处理完数组中的数据,如何释放内存呢?我能否在函数外部进行处理?通过分配大块数据进行数据处理比分配和删除小块数据性能更好吗?

非常感谢您的帮助!


新手应该学习使用std::stringstd::vector - Matthieu M.
8个回答

15

使用 delete[] 释放动态数组:

char* block = ReadBlock(...);
// ... do stuff
delete[] block;

但理想情况下,您不在此处使用手动内存管理:

std::vector<char> ReadBlock(std::fstream& stream, int size) {
    std::vector<char> memblock(size);
    stream.read(&memblock[0], size);
    return memblock;
}

2
使用这种方法,当向量对象被返回时,它的复制构造函数会被调用吗(我的意思是,在某一点上存在两个实例)? - dreamlax
4
这句话的意思是:这取决于情况,很可能会触发命名返回值优化(NRVO),请参考有趣的文章《想要速度吗?通过值传递。》(http://cpp-next.com/archive/2009/08/want-speed-pass-by-value/)。请注意,这些术语需要熟悉C++编程语言。 - Georg Fritzsche
如果不像上面的例子那么简单,我会始终使用BOOST shared_ptr(或intrusive_ptr,如果数据类型是类)。这听起来可能有些笨重,但可以导致一致的无泄漏代码(几乎)。 - deepsnore
@hack: 然后shared_array<T>在这里会更合适,不需要自定义删除器。 - Georg Fritzsche
1
@dream:是的,这是一个不改变可观察行为规则的显著例外。例如,在使用X f() { return X(); } X x = f();(假设已激活优化)时,可以在构造函数和复制构造函数中使用日志语句来观察RVO。如果您需要标准方便,请参阅§12.8/15 - Georg Fritzsche
显示剩余2条评论

5

当你使用完这个函数的返回值后,只需要delete[]它即可。不必担心在外部删除会有什么问题,但在使用完成之前不要删除它。


2
必须使用 delete[],而不是 delete - Georg Fritzsche
1
哎呀!可悲的是,如果你犯了这个错误,编译器甚至无法检测到它。多么糟糕的语言啊。不管怎样,谢谢。我会修复这篇文章。(虽然必须说,我并没有提出整行代码,只是要使用内置函数。) - sigfpe

4
你可以调用以下内容:

char * block = ReadBlock(stream, size);
delete [] block;

但是...这样做会产生很多堆分配,却没有任何好处。考虑采用以下方法:

char *block = new char[size];
while (...) {
  stream.read(block, size);
}
delete [] block;

*注意,如果size可以是编译时常量,您可以只需堆栈分配block

1
可变大小的数组不是ISO C++标准,而是GCC扩展。 - Clark Gaebel
函数的定义表明:char* ReadBlock(..., int size); - Clark Gaebel
@wowus:这将消除函数的必要性,无论如何,收到意见了...已评论。 - Stephen
如果我不删除已读取的内存块,我会不会耗尽内存? - Emer
Stephen,为什么这变成了new int[size]而不是new char[size] - Georg Fritzsche
@emerrf:如果您的应用程序允许重复使用同一缓冲区(从您的问题中无法确定),则可以重复使用它。如果不可能重复使用同一缓冲区,则应使用其他方法,例如返回一个字符串。 - Stephen

2

我有一个类似的问题,并编写了一个简单的程序来演示为什么在函数外调用delete []仍将释放在函数内分配的内存:

#include <iostream>
#include <vector>

using namespace std;

int *allocatememory()

{
    int *temppointer = new int[4]{0, 1, 2, 3};
    cout << "The location of the pointer temppointer is " << &temppointer << ". Locations pointed to by temppointer:\n";
    for (int x = 0; x < 4; x++)
        cout << &temppointer[x] << " holds the value " << temppointer[x] << ".\n";
    return temppointer;
}

int main()
{
    int *mainpointer = allocatememory();
    cout << "The location of the pointer mainpointer is " << &mainpointer << ". Locations pointed to by mainpointer:\n";
    for (int x = 0; x < 4; x++)
        cout << &mainpointer[x] << " holds the value " << mainpointer[x] << ".\n";

    delete[] mainpointer;
}

这是我在终端上运行程序后得到的结果:
指针temppointer的位置为0x61fdd0。temppointer指向的位置:
0xfb1f20存储值0。
0xfb1f24存储值1。
0xfb1f28存储值2。
0xfb1f2c存储值3。
指针mainpointer的位置为0x61fe10。mainpointer指向的位置:
0xfb1f20存储值0。
0xfb1f24存储值1。
0xfb1f28存储值2。
0xfb1f2c存储值3。
这个读取结果说明,尽管temppointer(在allocatememory函数中创建)和mainpointer具有不同的值,它们指向的内存位置相同。这说明为什么调用delete[]来释放mainpointer也将释放temppointer所指向的内存,因为它们指向同一位置。

1
是的。你可以在函数外部调用delete。不过,在这种情况下,我建议你使用std::string,这样你就不必担心管理问题了。

好的,但是由于读取操作,我不能使用字符串。 istream&read(char * s,streamsize n); - Emer
然后使用向量代替。抱歉,没有注意到。 - Clark Gaebel

1

首先需要注意的是:使用 new 和 delete 分配的内存完全是全局的。当指针超出作用域或函数退出时,东西不会自动删除。只要您有分配的指针(例如在那里返回指针),您可以随时随地删除它。关键是确保其他东西不会在您不知道的情况下删除它。

这就是 fstream 读取函数具有的一种函数结构的好处。它相当清楚,所有该函数要做的就是将“size”字节数读入您提供的缓冲区中,无论该缓冲区是使用 new 分配的,还是静态的或全局缓冲区,甚至是本地缓冲区,或者仅是指向本地结构的指针。而且,该函数在将数据读入缓冲区后不会再对其进行任何处理。

另一方面,看看你的ReadBlock函数的结构;如果你没有它的代码,那么弄清楚它到底返回了什么会很棘手。它是返回一个指向新分配内存的指针吗?如果是,它是否期望你删除它?它会自己删除吗?如果是,何时删除?它甚至是一个新指针吗?它只是返回一个指向某个共享静态缓冲区的地址吗?如果是,那么缓冲区何时会变得无效(例如被其他东西覆盖)?
查看ReadBlock的代码,很明显它返回一个指向新分配内存的指针,并且期望你在使用完后将其删除。该缓冲区在你删除它之前永远不会被覆盖或变得无效。
就速度而言,fsream.read的“你自己解决缓冲区”的方法还有另一个优点:你可以选择何时分配内存。如果你正在“读取数据、处理、删除缓冲区、读取数据、处理、删除缓冲区”等操作,那么只需分配一个缓冲区(最大尺寸为你最大的单次读取大小),并将其用于所有操作,这将更加高效,正如Stephen所建议的那样。

0

自C++11以来,可以使用std::unique_ptr来实现此目的。

来源于https://en.cppreference.com/book/intro/smart_pointers

void my_func()
{
    int* valuePtr = new int(15);
    int x = 45;
    // ...
    if (x == 45)
        return;   // here we have a memory leak, valuePtr is not deleted
    // ...
    delete valuePtr;
}

但是,

#include <memory>
 
void my_func()
{
    std::unique_ptr<int> valuePtr(new int(15));
    int x = 45;
    // ...
    if (x == 45)
        return;   // no memory leak anymore!
    // ...
}

0

使用静态char* memblock如何?它只会被初始化一次,不会每次分配新的空间。


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