将C ++ new运算符与函数一起使用的智能方法是什么?

时间:2011-06-15 18:05:02

标签: c++ function scope new-operator

这是一个很长的问题。我有一个从磁盘加载一些图像数据的功能。我用三种不同的方法尝试过这种方法,其中一种方法不起作用,我想知道最聪明的方法是什么。

方法1:

我曾经设置它,以便加载的数据是函数的参数,这似乎需要在函数外部为它分配空间,然后将指针传递给分配的空间。这需要提前知道图像的大小,因此必须首先获得辅助函数以获得图像的宽度和高度。以下是一个例子。

优点:在调用代码中更明确地分配内存,因此应该删除。

缺点:需要提前知道图像尺寸。

// Data allocated outside the image, allocated space passed to function. This works.
// Notice that width & height are passed to the function. 
size=(*width)*(*height);
image  = new unsigned char[size];

void read_pgm(unsigned char *image, char *file_name, int width, int height){

    // Code to read sizeof(char)*width*height bytes of data from the file into image

}

方法2:

我认为让函数分配自己的数据会很好,所以我不需要传递它的大小。如果我试图在函数中为它分配空间,它在函数结束后似乎丢失了。在下面的例子中,函数read_pgm运行正常,但如果我然后尝试将该数据写入另一个文件,我的代码就会崩溃。

优点:不需要提前知道图像大小,不需要在调用代码中分配数据。

缺点:不起作用。另外,如果确实如此,如果我没有在函数外部清除图像,那么在循环中反复调用它会导致内存泄漏吗?

// Data allocated inside the image for a pointer passed to the function. This doesn't work.
void read_pgm(unsigned char *image, char *file_name, int *width, int *height){

    size=(*width)*(*height);
    image  = new unsigned char[size];

    // Code to read the data from the file into image

}

方法3:

这里,数据再次在函数中分配,但作为返回项目返回。这是有效的,即我可以将数据写入另一个图像。我不明白为什么这样做而方法2没有。

优点:与方法2相同。

缺点:与方法2相同,但目前无效。

// Data allocated in the function, and returned. This works.
unsigned char* read_pgm(char *file_name, int *width, int *height){

    // Allocate data for the image
    size=(*width)*(*height);
    image  = new unsigned char[size];

    // Code to read the data from the file into image

    return image; // Return pointer to the data
}

所以,我的问题是:

  1. 在这样的情况下,将函数设置为自己分配空间是否更智能,因此调用代码不需要提供图像大小?或者在函数外部分配更聪明,提醒您需要在某个时刻在图像上调用删除。或者我错误地认为需要这样做?似乎在循环中调用方法2或方法3会导致内存泄漏。

  2. 为什么方法2不起作用?

  3. 感谢。

7 个答案:

答案 0 :(得分:5)

如果你想知道聪明的方法,那么答案就必须是“以上都不是”。聪明的方式?使用矢量。这就是它的用途。因为直接使用new很糟糕。管理自己内存的界限很糟糕。我们有课程。和char*字符串?至少使它成为const char*const std::string&更好。我还要问 - 你试图读取的图像格式是什么,不能以文件格式存储图像的宽度和高度?在我看来,你最好从文件中读取它。

std::vector<unsigned int> void ReadImage(const std::string& filename, int width, int height) {
    std::vector<unsigned int> imageData(width * height);
    // Read here from filestream
}

std::vector<unsigned int> imageData = ReadImage("ohai.png", 1000, 600);

您需要了解 - const正确性,RAII和标准库。

答案 1 :(得分:1)

回答问题2

为了使其工作,您必须传递对指针的引用

unsigned char * & image

否则,您将分配内存,并且传递的指针的COPY指向它。原始指针对象不会改变。

回答问题1

有没有听说过smart pointers

智能指针可用于自行操作内存。如果你不想出于一些莫名其妙的原因使用智能指针,比如伪优化,那么我认为你自己描述了所有方法的优点和缺点 - 总是权衡。由您决定

答案 2 :(得分:0)

方法2不起作用,因为您按值传递指针,并在本地覆盖该值,因此函数外部的指针值不会更改。您可以通过引用传递它或将指针传递给指针来解决此问题。

至于另一个问题:这只是一个明确记录谁有责任删除已分配数据的问题。 (顺便说一下:是的,你应该解除分配,否则你会泄漏)。在许多API中,您将在文档中看到:“调用者有责任删除此”或“调用此其他函数来删除已分配的数据”。

解决这种所有权问题的一个好方法是使用智能指针(如Armen建议的那样)。

答案 3 :(得分:0)

您目前所做的一切都会导致内存泄漏,因为您永远不会delete[]

首先,是的,使维度变量。其次,返回指向新分配的堆存储的指针,记住在调用者中删除它,或者返回智能容器对象。

std::vector<unsigned char>会做得很好:

std::vector<unsigned char> get_image(const std::string & filename, size_t & width, size_t & height)
{
  // determine width and height

  /* ... */

  std::vector<unsigned char> result(width * height, 0);

  // read into &result[0], vector guarantees contiguous storage

  return result;
}

答案 4 :(得分:0)

考虑封装与阅读和“处理”类中数据相关的所有功能。

它应该将文件路径作为构造函数中的std :: string。

创建文件句柄,分配内存和读取可以在单独的函数中完成(类似Init)。这使您能够在知道文件路径时创建对象,但稍后在实际需要时执行耗时的部分(读取部分)。

您可以向外部用户公开所需的任何信息(例如高度或宽度)。

类析构函数将负责关闭文件句柄(如果需要,可以提前完成)并取消分配数据。

您可以在课堂上使用new和delete,或者更好的是,在课堂上使用智能指针而不仅仅是图像数据部分。您也可以“在堆栈上”使用它,并在超出范围时自动调用它的析构函数。

答案 5 :(得分:-1)

“2。为什么方法2不起作用?”

因为您是按副本传递指针,而不是通过引用传递指针。传入一个指针,然后提示重新初始化,指针指向,并使用new调用。当达到函数的范围时,局部变量image的生命也是如此,以及所有可爱的新存储都可以使用。

你真正想要做的是(在这种情况下),是传递指针的引用还是指向指针的指针,这样你就可以改变传递的参数的值(这种情况下是指针),并且变化在函数范围之外可见。

void read_pgm(unsigned char **image, char *file_name, int *width, int *height)
{      
    size=(*width)*(*height);     
    (*image)  = new unsigned char[size];      

    // Code to read the data from the file into image  
} 

答案 6 :(得分:-1)

C++ FAQBook对这些问题实际上有一个非常出色的部分。

正如您所说,最大的问题是确保以确保内存不泄漏的方式分配内存。 C ++有很多方便的工具,最明显的是newdelete

最简单的方法是在一个超出范围的上下文中使用new分配storate。所以......

 {
     MyData *md = new MyData(args);

     doSomething(md);

 }

现在,当md超出范围时,它将自动调用dtor。现在,这个技巧几乎无处不在。

使用你的方法2,你写的是正确的,它会泄漏内存;下次执行new时,对最后一次的引用将丢失,但尚未最终确定或销毁。

解决方案是明确调用dtor,即在其他地方删除它。

我怀疑它不起作用的原因是image是一个指针,这是新想要返回的指针。但是由于C ++按指令调用指针,你将该地址放入堆栈的本地内存中,它就会消失。

您要么需要**,要么更需要参考。

第三种解决方案是C ++确实有指针类型来帮助进行免费商店管理。 FAQbook描述了如何实现引用计数指针here