[cpp]C++中的析構函數

C++中的析構函數

簡介

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

  • 函數執行結束
  • 程序執行結束
  • 程序塊包含的局部變量
  • delete操做

何時要本身寫析構函數?

編譯器會自動建立默認的析構函數,一般都沒有問題,可是當咱們在類中動態分配了內存空間時,咱們須要手段的回收這塊空間,防止內存溢出。就像這樣c++

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,設置時沒有問題的,可是一個問題就是,一般的手段就無法調用析構函數了。程序員

以下所示,程序結束後要調用析構函數,可是析構函數時私有的無法調用,因此會編譯出錯。ide

#include <iostream> 
using namespace std; 
class Test { 
private: 
	~Test() {} 
}; 
int main() 
{ 
	Test t; 
}

如下這樣不會有問題,由於沒有對象被創建,也不用析構函數

int main() 
{ 
    Test* t;                                          
}

如下這樣也不會有問題,由於動態分配的內存須要程序員手段釋放,因此程序結束時沒有釋放內存,也沒有調用析構函數。這裏插一句,動態分配的內存若是不手動釋放,程序結束後也會不會釋放,可是現代操做系統能夠幫咱們釋放,由於這個動態分配的內存和這個進程有關,操做系統應該能夠捕獲到這個泄露的內存從而釋放。(查資料看到的)this

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

若是使用delete來刪除對象,會編譯出錯spa

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

能夠利用Friend函數,進行對象的銷燬,由於Friend能夠訪問私有成員,因此能夠訪問析構函數。操作系統

#include <iostream> 

class 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() {} 
};

那麼何時須要使用私有的析構函數呢?當咱們只但願動態分配對象空間(在堆上)時候,用私有析構,就防止了在棧上分配,由於在編譯階段就會出錯。code

虛析構函數

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

#include <iostream>
using namespace std;
class Base
{
public:
    Base(){
        cout << "Base Constructor Called\n";
    }
    ~Base(){
        cout << "Base Destructor called\n";
    }
};
class Derived1: public Base
{
public:
    Derived1(){
        cout << "Derived constructor called\n";
    }
    ~Derived1(){
        cout << "Derived destructor called\n";
    }
};
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/
相關文章
相關標籤/搜索