这是一个很长的问题。我有一个从磁盘加载一些图像数据的功能。我用三种不同的方法尝试过这种方法,其中一种方法不起作用,我想知道最聪明的方法是什么。
方法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
}
所以,我的问题是:
在这样的情况下,将函数设置为自己分配空间是否更智能,因此调用代码不需要提供图像大小?或者在函数外部分配更聪明,提醒您需要在某个时刻在图像上调用删除。或者我错误地认为需要这样做?似乎在循环中调用方法2或方法3会导致内存泄漏。
为什么方法2不起作用?
感谢。
答案 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)
为了使其工作,您必须传递对指针的引用
unsigned char * & image
否则,您将分配内存,并且传递的指针的COPY指向它。原始指针对象不会改变。
有没有听说过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 ++有很多方便的工具,最明显的是new
和delete
。
最简单的方法是在一个超出范围的上下文中使用new
分配storate。所以......
{
MyData *md = new MyData(args);
doSomething(md);
}
现在,当md超出范围时,它将自动调用dtor。现在,这个技巧几乎无处不在。
使用你的方法2,你写的是正确的,它会泄漏内存;下次执行new
时,对最后一次的引用将丢失,但尚未最终确定或销毁。
解决方案是明确调用dtor,即在其他地方删除它。
我怀疑它不起作用的原因是image
是一个指针,这是新想要返回的指针。但是由于C ++按指令调用指针,你将该地址放入堆栈的本地内存中,它就会消失。
您要么需要**
,要么更需要参考。
第三种解决方案是C ++确实有指针类型来帮助进行免费商店管理。 FAQbook描述了如何实现引用计数指针here。