1

在 C/C++ 中似乎是一个常识,在函数完成之前new/malloc应该有一些内存的函数,对吧?delete/free

但是假设我有这样的问题,有一个函数reader会从文件中读取块到缓冲区,还有另一个函数consumer会消耗这些缓冲区,

void reader(ifstream &ifs)
{
    char *buf = new char[100];
    ifs.read(buf, 100);
    save_buffer(buf);  //save this buf into a global array, say buf_array[10]
}

void consumer()
{
    for(int i = 0; i < 10; i++)
        consume( buf_array[i] );  //do something with those buffers read by reader
}

我的问题是,很多内存资源都new在里面reader,但reader不能delete,因为这些缓冲区没有被consumer. 应该consumer负责deleteing 那些缓冲区?

4

9 回答 9

4

没有人说分配内存的函数应该释放内存。但通常相同的组件应该处理它。因为你reader和你consumer结成一对,他们可以把记忆协调在一起。

于 2012-07-26T02:36:53.107 回答
1

在 C/C++ 中似乎是一个常识,在函数完成之前new/malloc应该有一些内存的函数,对吧?delete/free

不,这不一定是真的,你不必在同一个函数中释放内存,只要你最终在程序结束之前释放它。

C++ 中可用的一种常见解决方案(但在 C 中不可用)是在析构函数中释放内存。如果在正确处理复制/移动构造函数和赋值运算符时传递包含动态分配内存的对象,则调用析构函数时将释放内存。

于 2012-07-26T02:37:16.907 回答
1

原则是“对于每一个new,应该有一个delete”。这并没有说明两个调用必须在同一个函数中(显然,这不是很有用)。

您的示例在读者分配和消费者释放方面没有问题。

于 2012-07-26T02:37:34.420 回答
1

您不必在同一个函数中执行new/malloc(分配)和(释放)。delete/free只要您的算法保证每个分配都被释放并且只被释放一次,从而不会导致内存泄漏,就可以了。
事实上,分配和释放通常存在于不同的函数中。
请记住以下几点:
1. 使用相同的指针进行释放(当然您可以传递指针)。如果您对指针进行一些算术运算,然后使用修改后的指针释放分配,即使指针仍然指向分配区域,也会产生错误。
2. 如上所述,您应该保证分配被释放,并且只释放一次。附加版本会导致错误。

于 2012-07-26T02:38:33.607 回答
1

您不需要在初始化它的同一函数中释放分配的缓冲区,只要您携带指向缓冲区的指针即可。

在您的情况下,consumer()应该负责delete分配由consumer.

至于你的第二个问题,消费者不知道缓冲区在哪里结束;你需要以某种方式告诉它。struct您可以考虑定义一个新的封装缓冲区及其长度的指针,而不是仅仅存储指向缓冲区的指针。这已经完成:考虑使用std::string.

于 2012-07-26T02:39:10.333 回答
1

其他人的答案略有不同:

是的,阅读器不释放/删除阅读功能中的内存很好,但我不会让消费者删除内存。在一个简单的情况下可行,但如果您有多个消费者(例如以不同格式输出)怎么办?如果消费数据有释放它的副作用,那么在第一个消费者完成它之后,你就不能对数据做任何其他事情了。

我会cleanup()在我的阅读器中有一个类型方法,我会在需要时显式调用它来清理缓冲区。这样分配内存的模块负责释放它(即使它使用不同的方法)。

例如

Data d = Reader.read();
Consumer1.consume(d);
Consumer2.consume(d);
Reader.cleanup(d);
// d is no longer valid.
于 2012-07-26T02:47:18.373 回答
1

您正在将 buf 的内容复制到一个全局数组 buf_array ,该数组实际上是为消费者提供的。因此,在上面的示例中,读者可以释放 buf。

而且,不需要 new/mallocs 的函数释放内存。指针可以传递。最后使用分配的内存的函数需要释放它的内存。

于 2012-07-26T02:47:29.753 回答
0

按照偏好的降序排列,您希望:

  1. 使用auto存储类,因此删除会自动发生。
  2. 使用相同的代码分配和删除对象。
  3. 传递所有权,因此一个组件分配而另一个组件释放。
  4. 通过类似的东西共享所有权shared_ptr

3 和 4 几乎并列,但都比 2 落后很多,而 2 也比 1 落后很多。

在这种情况下,您真正​​希望的是在生产者和消费者之上的函数级别(或类等)分配缓冲区,启动生产者和消费者,并在两者都完成时删除缓冲区:

class buffer { 
     std::vector<char> data;
public:
     buffer() : data(100) {}
};

process_file(std::string const &name) { 
    std::vector<buffer> buffers(10);

    std::ifstream in(name);

    std::pair<std::ifstream *, std::vector<buffer> *> thread_data = {
        &in, &buffers
    };

    prod = start_thread(producer, (void *)&thread_data);
    cons = start_thread(consumer);
    join(prod);
    join(cons);
}

如果你能做到这一点,它可以帮助避免很多管理内存的麻烦。

于 2012-07-26T03:49:06.967 回答
0

您还可以阅读有关共享数组http://www.boost.org/doc/libs/1_35_0/libs/smart_ptr/shared_array.htm

使用共享数组,您可以在函数内分配它们并按值将它们传递给其他函数。共享数组保持内部计数它被引用的次数,并在所有引用超出范围时自动删除内存。

例如

void bar( boost::shared_array< int > );

void foo()
{
    boost::shared_array< int > x ( new int[ 100 ] );
    bar( x );
    // because x has been passed to bar(), its reference count is incremented
    // and it will not only be deleted when everyone is finished with it!

    // do more things with x
} // the memory held by x is deleted

void bar( boost::shared_array< int > y )
{
    // Do things with y here
} // the memory held by y is not deleted here because foo() hasn't yet finished
于 2012-07-26T03:29:02.760 回答