2012-10-05 94 views
6

我遇到了在子例程結束時調用析構函數的問題,即使它應該在子例程範圍之外定義。C++:析構函數在超出範圍之前被調用?

這裏是最小的代碼我有一個顯示我的問題:

#include <iostream> 
using namespace std; 

class Foo { 
private: 

    double *array; 

public: 

Foo(int N) { 
    array = new double[N]; 
    for (int i=0; i<N; i++) { 
     array[i]=0; 
    } 
} 

~Foo() { 
    delete[] array; 
} 
}; 

void subroutine(Foo x) { 
    cout << "Hello!" << endl; 
} 

int main() { 
    Foo bar(10); 
    subroutine(bar); 
    subroutine(bar); 
} 

現在對於對象條這裏獲得的第一個子程序之後調用析構函數中完成,即使它的範圍應該是整個的主功能?這意味着當我調用第二個子例程時,會再次調用析構函數,並且發生內存泄漏。

我發現我可以通過在子程序中引用來解決這個問題,但我不是很滿意這個修補程序,因爲我不明白它爲什麼不起作用。 任何人都可以爲我闡述一些這方面的信息嗎?

謝謝。

+2

鑑於您的析構函數,您需要定義或刪除'Foo'的複製構造函數和複製賦值運算符。搜索「三個規則」。 –

+5

「爲類調用析構函數」 - 您會發現,隨着時間的推移,如果您一直區分**類**和** **對象**,事情會變得更加清晰。析構函數在**對象**上不在類上調用。 –

回答

21

您正在向subroutine函數傳遞一個Foo值。這意味着它有自己的副本,在退出它的範圍時會被破壞。

void subroutine(Foo x) { 
    // x is a new Foo here. It will get destroyed on exiting scope, 
    // resulting in a destructor call 
} 

你這裏的主要問題是,你還沒有實現一個拷貝構造函數,所以動態分配的數組不復制(僅指向它的指針)。因此,當您複製Foo對象時,每個副本都指向相同的數組。每個副本都會試圖摧毀它。

您應該遵循rule of three並實現一個賦值操作符和一個拷貝構造函數,以便對該數組進行「深度拷貝」,以使每個Foo對象擁有自己的數組。

+0

嗯謝謝你的快速回復!是的,我注意到通過引用調用固定它,但我不知道爲什麼,感謝解釋! – Plog

+1

@ user1722882:其實,你最好使用'std :: vector'並完全移除你的析構函數。重新實現標準庫容器可能對它的運動有好處,但是當它不是這個練習/工作的主要目的時......它只會導致生產力的喪失。 –

1

您遇到的問題是,你是按值傳遞的對象:

void subroutine(Foo x) { 

這是創建一個臨時的對象,並調用您的對象的每個調用它的拷貝構造函數/析構函數。

3

當您調用void subroutine(Foo x) {時,將複製對象bar(因此在函數完成後調用析構函數)。

嘗試使用:void subroutine(Foo &x) {,它應該工作得很好。

6

您正在將值傳遞給子例程,以便創建副本。爲了避免複製按引用傳遞它:

void subroutine(Foo& x) 
{ 
    cout << "Hello!" << endl; 
} 

您可以防止類的意外副本通過聲明拷貝構造函數和拷貝賦值運算符私人像這樣:

class Foo { 
private: 

    double *array; 

    Foo(const Foo&); 
    Foo& operator=(const foo&); 

public: 
    ... 
}; 

然後你得到一個編譯錯誤。如果你真的需要能夠複製你的課程,那麼你實際上需要實現這些功能來執行「深層複製」(或更好地使用std::vector<float>,並讓它爲你管理內存,包括安全複製)。

+0

與C++ 11,最好將它們聲明爲'deleted' –

相關問題