深入解析C++中的構造函數和析構函數

深入解析C++中的構造函數和析構函數

構造函數:
在類實例化對象時自動執行,對類中的數據進行初始化。構造函數可以重載,可以有多個,但是隻能有一個缺省構造函數。

析構函數:
在撤銷對象佔用的內存之前,進行一些操作的函數。析構函數不能被重載,只能有一個。

調用構造函數和析構函數的順序:
先構造的後析構,後構造的先折構。它相當於一個棧,先進後出。

#include<iostream>
#include<string>
using namespace std;
class Student{
 public:
  Student(string,string,string);
  ~Student();
  void show();
 private:
  string num;
  string name;
  string sex;
};
Student::Student(string nu,string na,string s){
 num=nu;
 name=na;
 sex=s;
 cout<<name<<" is builded!"<<endl;
}
void Student::show(){
 cout<<num<<"\t"<<name<<"\t"<<sex<<endl;
}
Student::~Student(){
 cout<<name<<" is destoried!"<<endl;
}
int main(){
 Student s1("001","千手","男");
 s1.show();
 Student s2("007","綱手","女");
 s2.show();
 cout<<"nihao"<<endl;
 cout<<endl;
 cout<<"NIHAO"<<endl;
 return 0;
}

先構造的千手,結果後析構的千手;後構造的綱手,結果先折構的綱手。

特點:
在全局範圍定義的對象和在函數中定義的靜態(static)局部對象,只在main函數結束或者調用exit函數結束程序時,才調用析構函數。

如果是在函數中定義的對象,在建立對象時調用其構造函數,在函數調用結束、對象釋放時先調用析構函數。

#include<iostream>
#include<string>
using namespace std;
class Student{
 public:
  Student(string,string);
  ~Student();
  void show();
  string num;
  string name;
};
Student::Student(string nu,string na){
 num=nu;
 name=na;
 cout<<name<<" is builded!"<<endl<<endl;
}
void Student::show(){
 cout<<num<<"\t"<<name<<endl<<endl;
}
Student::~Student(){
 cout<<name<<" is destoried!"<<endl<<endl;
}
void fun(){
 cout<<"============調用fun函數============"<<endl<<endl; 
 Student s2("002","自動局部變量");//定義自動局部對象 
 s2.show();
 static Student s3("003","靜態局部變量");//定義靜態局部變量 
 s3.show();
 cout<<"===========fun函數調用結束=============="<<endl<<endl;
}
int main(){
 Student s1("001","全局變量");
 s1.show();
 fun();
 cout<<"\nthis is some content before the end\n";//這是一段位於main函數結束之前,函數調用之後的內容
 cout<<endl;
 return 0;
}




析構函數再探:爲什麼有的類的析構函數是虛函數?


以下內容轉載自:http://blog.163.com/cindy_zhoulixia/blog/static/7361720920083229403237/

所有C++程序員對析構函數都不陌生,由於其簡單且易理解,所以都能很快應用。這裏我不說這些常用方法,若不知可參考C++書籍。而我這次所想說的是較微妙的技巧,常不被人注意,但卻非常非常的重要。看以下代碼:

#include <iostream.h>
class CFunction
{
public:
CFunction()
{

data = new char[64];
};
~CFunction()
{
delete [] data;
};

char *data;
};
class CFunctionEx : public CFunction
{
public:
CFunctionEx()

{
m_data = new char[64];
};
~CFunctionEx()
{
delete [] m_data;

};
private:
char *m_data;
};
void main()
{
CFunction *pCFun = new CFunctionEx;
delete pCFun;
}

你能看出什麼問題嗎?很顯然,有內存泄漏。這是因爲當刪除pCFun時,它只調用了Cfunction的析構函數而沒調用CfunctionEx的析構函數,所以導致內存泄漏。再看下例:

#include <iostream.h>

class CBase

{

public:

CBase()

{

data = new char[64];

};

~CBase()

{

delete [] data;

};

char *data;

};

class CFunction

{

public:

CFunction(){};

~CFunction(){};

};

class CFunctionEx : public CFunction

{

public:

CFunctionEx(){};

~CFunctionEx(){};

private:

CBase m_cbase;

};

void main()

{

CFunction *pCFun = new CFunctionEx;

delete pCFun;

}

你能看出什麼問題嗎?這裏CfunctionEx和Cfunction中本身並沒有分配內存,應該不會有內存泄漏。和上例一樣當刪除pCFun時,它只調用了Cfunction的析構函數而沒調用CfunctionEx的析構函數,但CfunctionEx本身並沒分配內存,是什麼地方有內存泄漏我不說大家也應該知道了吧。不錯是m_cbase,因爲它是Cbase的實例且是CfunctionEx成員變量,當CfunctionEx的的析構函數沒有被調用時,當然m_cbase的析構函數也沒有被調用,所以Cbase中分配的內存被泄漏。

解決以上問題的方法很簡單,就是使基類Cfunction的析構函數爲虛函數就可以了。很簡單,是嗎?哈哈……

這樣就得出一個結論,當你的基類的析構函數不爲虛的話, 其子類中所有的成員變量的類中分配的內存也將可能泄漏。

這一點非常重要,因爲很容易被遺漏。我就是爲此這才寫此文。

這裏說的可能是因爲,如果程序中沒有以上示例類似寫法(指用基類指針指向子類實例裕,虛函數是C++的精華,很少有人不用的,由其是在大中型軟件項目中),就不會出現本文所說的內存泄漏。看來在基類中使析構函數爲虛函數是如此的重要。所以強烈建議在基類中把析構函數聲明爲虛函數,但是隻有你寫的類並不做爲基類時例外。

以上我在工作中碰到的問題,程序在VC++6中測試,內存泄漏對於一個高效的服務程序來說十分重要。我想可能大家也可能出現過這種問題,所以寫出這篇文章,希望能給大家帶來幫助。

再舉一例

轉載自:http://blog.csdn.net/starlee/article/details/619827

我們知道,用C++開發的時候,用來做基類的類的析構函數一般都是虛函數。可是,爲什麼要這樣做呢?下面用一個小例子來說明:    
    有下面的兩個類:

#include<iostream>

using namespace std;

class ClxBase
{
public:
    ClxBase() {};
    virtual ~ClxBase() { cout << "Output from the destructor of class ClxBase!" << endl; };

    virtual void DoSomething() { cout << "Do something in class ClxBase!" << endl; };
};

class ClxDerived : public ClxBase
{
public:
    ClxDerived() {};
    ~ClxDerived() { cout << "Output from the destructor of class ClxDerived!" << endl; };

    void DoSomething() { cout << "Do something in class ClxDerived!" << endl; };
};

int main()
{
    ClxBase *pTest = new ClxDerived;
    pTest->DoSomething();
    delete pTest;

    return 0;
}


輸出結果是:

Do something in class ClxDerived!
Output from the destructor of class ClxDerived!
Output from the destructor of class ClxBase!

        但是,如果把類ClxBase析構函數前的virtual去掉,那輸出結果就是下面的樣子了:

Do something in class ClxDerived!
Output from the destructor of class ClxBase!

也就是說,類ClxDerived的析構函數根本沒有被調用!一般情況下類的析構函數裏面都是釋放內存資源,而析構函數不被調用的話就會造成內存泄漏。我想所有的C++程序員都知道這樣的危險性。當然,如果在析構函數中做了其他工作的話,那你的所有努力也都是白費力氣。
        所以,文章開頭的那個問題的答案就是--這樣做是爲了當用一個基類的指針刪除一個派生類的對象時,派生類的析構函數會被調用。
        當然,並不是要把所有類的析構函數都寫成虛函數。因爲當類裏面有虛函數的時候,編譯器會給類添加一個虛函數表,裏面來存放虛函數指針,這樣就會增加類的存儲空間。所以,只有當一個類被用來作爲基類的時候,才把析構函數寫成虛函數。




發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章