簡體   English   中英

返回指向函數聲明的數據的指針

[英]return pointer to data declared in function

我知道這不起作用,因為變量x在函數返回時被銷毀:

int* myFunction()
{
    int x = 4; return &x;
}

那么如何正確地返回指向我在函數中創建的東西的指針,以及我需要注意什么? 如何避免內存泄漏?

我也用過malloc:

int* myFunction2()
{
    int* x = (int*)malloc(sizeof int); *x = 4; return x;
}

你是如何正確地做到這一點的 - 在C和C ++中?

對於C ++,您可以使用智能指針來強制執行所有權轉移。 auto_ptrboost::shared_ptr是不錯的選擇。

你的第二種方法是正確的。 您只需要清楚地記錄調用者“擁有”結果指針,並負責釋放它。

由於這種額外的復雜性,很少為像“int”這樣的“小”類型執行此操作,盡管我假設您在這里僅使用了一個int作為示例。

有些人還希望將指向已分配對象的指針作為參數,而不是在內部分配對象。 這使得更清楚的是調用者負責釋放對象(因為他們首先分配它),但是使調用站點更加冗長,所以這是一個權衡。

對於C ++,在許多情況下,只需按值返回。 即使在較大物體的情況下, RVO也會經常避免不必要的復制。

一種可能性是將函數傳遞給指針:

void computeFoo(int *dest) {
    *dest = 4;
}

這很好,因為你可以使用這樣一個自動變量的函數:

int foo;
computeFoo(&foo);

使用這種方法,您還可以將內存管理保留在代碼的相同部分,即。 你不能錯過malloc只是因為它發生在函數內的某個地方:

// Compare this:
int *foo = malloc(…);
computeFoo(foo);
free(foo);

// With the following:
int *foo = computeFoo();
free(foo);

在第二種情況下,你更容易忘記免費,因為你沒有看到malloc。 這通常至少部分通過約定來解決,例如:“如果函數名稱以XY開頭,則表示您擁有它返回的數據。”

返回指向“function”變量的指針的一個有趣的角落情況是聲明變量static:

int* computeFoo() {
    static int foo = 4;
    return &foo;
}

當然這對於正常編程來說是邪惡的,但它有朝一日可能會派上用場。

C ++方法可以避免內存泄漏。 (至少在忽略函數輸出時)

std::auto_ptr<int> myFunction() {
    std::auto_ptr<int> result(new int(4));
    return result;
}

然后叫它:

std::auto_ptr<int> myFunctionResult = myFunction();

編輯:正如喬爾所指出的那樣。 std :: auto_ptr有它自己的缺點,通常應該避免。 而不是std :: auto_ptr你可以使用boost :: shared_ptr(std :: tr1 :: shared_ptr)。

boost::shared_ptr<int> myFunction() {
    boost::shared_ptr<int> result(new int(5));
    return result;
}

或者當使用C ++ 0x符合編譯器時您可以使用std :: unique_ptr。

std::tr1::unique_ptr<int> myFunction() {
    std::tr1::unique_ptr<int> result(new int(5));
    return result;
}

主要區別在於:

  • shared_ptr允許多個shared_ptr實例指向同一個RAW指針。 它使用引用計數機制來確保只要存在至少一個shared_ptr實例就不會釋放內存。

  • unique_ptr只允許其中一個實例持有指針,但與auto_ptr不同,它具有真正的移動語義。

在C ++中,您應該使用new

int *myFunction()
{
    int blah = 4;
    return new int(blah);
}

要擺脫它,使用刪除:

int main(void)
{
    int *myInt = myFunction();
    // do stuff
    delete myInt;
}

請注意,我在使用new調用int的復制構造函數,因此將值“4”復制到堆內存中。 獲取指向堆棧上某些東西的指針的唯一方法是通過正確調用new將其復制到堆上。

編輯:如另一個答案中所述,您還需要記錄稍后調用者需要釋放指針。 否則您可能會發生內存泄漏。

還有另一種方法 - 聲明x靜態。 在這種情況下,它將位於數據段中,而不是堆棧中,因此在程序運行時期間它是可用的(並且是持久的)。

int *myFunction(void)
{
    static int x = 4;
    return &x;
}

請注意,只有在第一次調用myFunction才會執行賦值x=4

int *foo = myFunction();   // foo is 4
*foo = 10;                 // foo is 10
*foo = myFunction();       // foo is 10

NB! 使用函數范圍靜態變量不是踏板安全技術。

您的第二個代碼段是正確的。

為了幫助避免內存泄漏,我讓編碼約定幫助我。

xxxCreate()將為xxx分配內存並初始化它。 xxxDelete()將破壞/損壞xxx並釋放它。

xxxInit()將初始化xxx(永不分配)xxxDestroy()將破壞/損壞xxx(永不免費)

另外,一旦我將代碼添加到create / init / malloc,我就會嘗試添加代碼來刪除/銷毀/釋放。 它並不完美,但我發現它可以幫助我區分需要釋放的物品和不需要物品的物品,以及減少我以后忘記釋放物品的可能性。

Boost或TR1共享指針通常是要走的路。 它避免了復制開銷,並為您提供半自動刪除。 所以你的功能應該是這樣的:

boost::shared_ptr<int> myFunction2()
{
    boost::shared_ptr<int> x = new int; 

    *x = 4; 
    return x;
}

另一種選擇就是允許復制。 如果對象很小(比如這個),那也不錯,或者你可以安排在return語句中創建對象。 如果在return語句中創建對象,編譯器通常會優化副本。

我會嘗試這樣的事情:

int myFunction2b( int * px )
{
  if( px )
  {
    *px = 4;
    return 1;
  }

  // Choice 1: Assert or Report Error
  // Choice 2: Allocate memory for x. Caller has to be written accordingly.

  // My choice is 1
  assert( 0 && "Argument is NULL pointer" );
  return 0;

}

你問的是如何正確返回一個指針。 這是錯誤的問題,因為你應該做的是使用智能指針而不是原始指針。 scoped_ptr和shared_ptr(在boost和tr1中可用)是很好的指針(例如這里這里

如果你需要原始指針(例如傳遞給C函數), get()方法將提供它。

如果你必須創建原始指針,例如用於家庭作業,那么你可以在函數中使用malloc() (如你所做的那樣)或new ,並希望你記得去除內存(分別通過free()delete )或者,在一個稍微不太可能泄密的習語中,你可以用new創建指針,將它傳遞給一個函數,並在你完成它時用delete刪除 但是,再次使用智能指針。

暫無
暫無

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

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