簡體   English   中英

將C ++ new運算符與函數一起使用的明智方法是什么?

[英]What's the smart way to use C++ new operator with a function?

這是一個很長的時間。 我有一個從磁盤加載一些圖像數據的功能。 我用三種不同的方法嘗試過這種方法,其中一種不起作用,我想知道最聰明的方法是什么。

方法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. 在這種情況下,將函數設置為自己分配空間是否更聰明,因此調用代碼不需要提供圖像大小? 還是在函數外部進行分配是更明智的選擇,以提醒您有時需要在圖像上調用delete。 還是我認為需要這樣做是錯誤的? 似乎在循環中調用方法2或方法3會導致內存泄漏。

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

謝謝。

如果您想知道聰明的方法,那么答案必須是“以上皆非”。 聰明的方法? 使用向量。 那就是它的目的。 因為使用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和標准庫。

問題2的答案

為了使其正常工作,您必須將引用傳遞給指針

unsigned char * & image

否則,您將分配內存,並且傳遞的指針的COPY指向該內存。 原始指針對象不變。

回答問題1

聽說過智能指針嗎?

智能指針可以用來獨自操縱內存。 如果您出於某些無法解釋的原因而不想使用智能指針,例如偽優化,那么我認為您自己描述了所有方法的優缺點-總是要權衡取舍。 由您決定

方法2不起作用,因為您要按值傳遞指針,並在本地覆蓋該值,因此函數外部的指針值不會更改。 您可以通過按引用傳遞它或將指針傳遞給指針來解決此問題。

至於另一個問題:這實際上只是清楚地記錄誰有責任刪除分配的數據。 (順便說一句:是的,您應該取消分配,否則會泄漏)。 在許多API中,您會在文檔中看到:“調用者有責任刪除此數據”或“調用此其他函數以刪除分配的數據”。

消除這種所有權問題的一種好方法是使用智能指針(如Armen所建議的)。

當前所做的所有操作都會導致內存泄漏,因為您從不會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;
}

考慮將與讀取和“處理”數據有關的所有功能封裝在一個類中。

它應該在構造函數中將文件路徑作為std :: string。

創建文件句柄,分配內存和讀取然后可以在單獨的函數中完成(類似於Init )。 這樣,您便可以在知道文件路徑時創建對象,但稍后在實際需要時執行耗時的部分(讀取部分)。

您可以向外部用戶公開任何必要的信息(例如身高或寬度)。

類析構函數將負責關閉文件句柄(如果需要,可以更早完成)並取消分配數據。

您可以在類中使用new和delete,或者更好的是,在類中使用智能指針,而不僅僅是圖像數據部分。 您還可以“在堆棧上”使用它,並在超出范圍時自動調用其析構函數。

“ 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  
} 

實際上, C ++ FAQBook中有一個非常出色的部分,針對這些問題。

正如您所說,最大的問題是確保以不會泄漏的方式分配內存。 C ++有很多方便的工具可以做到這一點,最明顯的是newdelete

最簡單的方法是在將超出范圍的上下文中為new分配。 所以...

 {
     MyData *md = new MyData(args);

     doSomething(md);

 }

現在,當md超出范圍時,它將自動調用dtor。 現在,這個技巧幾乎可以在任何地方使用。

使用方法2,您按原樣寫對了,它會泄漏內存; 下次執行new ,該上次的引用將丟失,但不會最終確定或銷毀。

一個解決方案是在其他地方顯式調用dtor,即將其刪除。

我懷疑它不起作用的原因是image是一個指針,這是new想要返回的指針。 但是由於C ++會執行按值調用的指針,因此您將該地址放入堆棧的本地內存中,然后它消失了。

您要么想要** ,要么甚至更好的參考。

第三種解決方案是C ++確實具有指針類型來幫助免費商店管理。 該FAQbook介紹了如何實現引用計數指針在這里

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM