歡迎您光臨本站 註冊首頁

詳解C++中的析構函數

←手機掃碼閱讀     ljg58026 @ 2020-06-21 , reply:0

簡介

析構函數(Destructors),是對象的成員函數,沒有返回值也沒有參數,且一個類只有一個析構函數,當對象被銷燬的時候調用,被銷燬通常有這麼幾個情況。

  • 函數執行結束

  • 程序執行結束

  • 程序塊包含的局部變量

  • delete操作

什麼時候要自己寫析構函數?

編譯器會自動創建默認的析構函數,通常都沒有問題,但是當我們在類中動態分配了內存空間時,我們需要手段的回收這塊空間,防止內存溢出。就像這樣

  class String   {   private:   	char *s;   	int size;   public:   	String(char *); // constructor   	~String();	 // destructor   };      String::String(char *c)   {   	size = strlen(c);   	s = new char[size+1];   	strcpy(s,c);   }      String::~String()   {   	delete []s;   }

 

私有的析構函數

可以將析構函數的訪問權限設置為private,設置時沒有問題的,但是一個問題就是,通常的手段就沒法調用析構函數了。

如下所示,程序結束後要調用析構函數,但是析構函數時私有的沒法調用,所以會編譯出錯。

  #includeusing namespace std;   class Test {   private:   	~Test() {}   };   int main()   {   	Test t;   }

 

以下這樣不會有問題,因為沒有對象被建立,也不用析構

  int main()   {    Test* t;             }

 

以下這樣也不會有問題,因為動態分配的內存需要程序員手段釋放,所以程序結束時沒有釋放內存,也沒有調用析構函數。這裡插一句,動態分配的內存如果不手動釋放,程序結束後也會不會釋放,但是現代操作系統可以幫我們釋放,因為這個動態分配的內存和這個進程有關,操作系統應該可以捕獲到這個洩露的內存從而釋放。(查資料看到的)

  int main()   {    Test* t = new Test;   }

 

如果使用delete來刪除對象,會編譯出錯

  int main()   {    Test* t = new Test;   delete t;//編譯出錯,無法調用私有的析構函數   }

 

可以利用Friend函數,進行對象的銷燬,因為Friend可以訪問私有成員,所以可以訪問析構函數。

  #includeclass Test {   private:   	~Test() {}   	friend void destructTest(Test*);   };     void destructTest(Test* ptr)   {   	delete ptr;   }     int main()   {   	Test* ptr = new Test;   	destructTest(ptr);     	return 0;   }

 

或者給類寫一個銷燬的方法,在需要銷燬的時候調用。

  class Test {   public:   destroy(){delete this};  private:   	~Test() {}   };

 

那麼什麼時候需要使用私有的析構函數呢?當我們只希望動態分配對象空間(在堆上)時候,用私有析構,就防止了在棧上分配,因為在編譯階段就會出錯。

虛析構函數

當類用到多態的特性時候,使用虛析構函數。看如下的例子。

  #includeusing namespace std;  class Base  {  public:   Base(){    cout << "Base Constructor Called ";   }   ~Base(){    cout << "Base Destructor called ";   }  };  class Derived1: public Base  {  public:   Derived1(){    cout << "Derived constructor called ";   }   ~Derived1(){    cout << "Derived destructor called ";   }  };  int main()  {   Base *b = new Derived1();   delete b;  }

 

例子裡的析構函數都不是虛函數,當我們想用基類的指針來刪除派生類對象的時候,就出現了問題,“undefined behavior”,c++標準裡規定,只由編譯器實現,通常這時不會報錯,會調用基類的析構函數。但這應該不是我們想要的,這會導致內存洩漏。所以要把析構函數置為虛函數。(msvc似乎不用給析構函數加virtual,默認就是虛的,gcc沒有默認還是要加的)

另外虛析構函數可以是純虛析構函數,但是要提供函數體,不然沒法析構,因為虛析構函數和一般的虛函數的overide還不一樣,虛析構函數要挨個執行,不提供函數體,會編譯出錯。

析構函數的執行順序

派生類,成員對象,基類這樣

  class B  {public: virtual ~B(){cout<<"基類B執行了"<<endl; }  };    class D  {public:virtual ~D(){cout<<"成員D執行了"<<endl; }  } ;    class E  {public:virtual ~E(){cout<<"成員E執行了"<<endl; }  } ;    class A  {public:virtual ~A(){cout<<"基類A執行了"<<endl;};   };    class C:public A,B  {   public:virtual ~C(){cout<<"派生類執行了"<<endl;};   private:    E e;    D d;  };    int main()   {    C *c;   c=new C();   delete c;  }

結果為

派生類執行了
 成員D執行了
 成員E執行了
 基類B執行了
 基類A執行了

參考

[1]什麼時候使用虛函數https://stackoverflow.com/questions/461203/when-to-use-virtual-destructors
 [2]析構函數https://www.geeksforgeeks.org/destructors-c/
 [3]虛析構函數https://www.geeksforgeeks.org/virtual-destructor/
 [4]純析構函數https://www.geeksforgeeks.org/pure-virtual-destructor-c/

                  

   


[ljg58026 ] 詳解C++中的析構函數已經有245次圍觀

http://coctec.com/docs/c/language/show-post-239370.html