C++ - 構造函數(shù)與析構函數(shù)

參考鏈接

一、構造函數(shù)

  • C++規(guī)定,每個類必須有默認的構造函數(shù),沒有構造函數(shù)就不能創(chuàng)建對象。
  • 若沒有提供任何構造函數(shù),那么c++提供自動提供一個默認的構造函數(shù),該默認構造函數(shù)是一個沒有參數(shù)的構造函數(shù),它僅僅負責創(chuàng)建對象而不做任何賦值操作。
  • 只要類中提供了任意一個構造函數(shù),那么c++就不在自動提供默認構造函數(shù)。
  • 類對象的定義和變量的定義類似,使用默認構造函數(shù)創(chuàng)建對象的時候,如果創(chuàng)建的是靜態(tài)或者是全局對象,則對象的位模式全部為0,否則將會是隨即的。
#include<iostream>
class Student{
  public:
  Student(){//無參數(shù)構造函數(shù)
  number = 1;
  score = 100;
  }
void show();
protected:
int number;
int score;
};
void Student::show(){
  cout<<number<<endl<<score<<endl;
}
void main()
{
  Student a;
  a.show();
  cin.get();
}
  • 在類中的定義和類名相同,并且沒有任何返回類型的Student()就是構造函數(shù),這是一個無參數(shù)的構造函數(shù),他再對象創(chuàng)建的時候自動調用,如果去掉 Student()函數(shù)體內的代碼那么它和 C++默認提供的構造函數(shù)是等價的。
  • 構造函數(shù)可以帶任意多個形式的參數(shù),這一點和普通函數(shù)的特性是一樣的!下面是一個帶參數(shù)的構造函數(shù):
#include<iostream>
using namespace std;
class Teacher
{
    public:
    Teacher(char * input_name)//有參數(shù)的構造函數(shù)
    {
        name = new char[10];
        //name = input_name;//這樣賦值是錯誤的
        strcpy(name,input_name);
    }
    void show();
protected:
char *name;
}
void Teacher::show()
{
    cout<<name<<endl;
}
void main()
{
    //Teacher a;//這里是錯誤的,因為沒有無參數(shù)的構造函數(shù)
    Teacher a("test");
    a.show();
    cin.get();
}
  • 兩個注意
    • 我們創(chuàng)建了一個帶有字符指針的帶有形參的 Teacher(char *input_name) 的構造函數(shù),調用它創(chuàng)建對象的使用類名加對象名稱加擴號和擴號內參數(shù)的方式調用,這和調用函數(shù)有點類似,但意義也有所不同,因為 構造函數(shù)是為創(chuàng)建對象而設立的,這里的意義不單純是調用函數(shù),而是創(chuàng)建一個類對象。
      一旦類中有了一個帶參數(shù)的構造函數(shù)而又沒有無參數(shù)構造函數(shù)的時候系統(tǒng)無法創(chuàng)建不帶參數(shù)的 Teacher a;
    • //name = input_name;//這樣賦值是錯誤的
      因為 name 指是指向內存堆區(qū)的,如果使用 name = input_name; 會造成指針指向改變不是指向堆區(qū)而是指向棧區(qū),導致在后面調用析構函數(shù) delete 釋放空間出錯!
#include <iostream>  
using namespace std;    
class Teacher  
{  
    public:  
    Teacher(char *input_name)  
    {  
        name=new char[10];  
        //name=input_name;//這樣賦值是錯誤的  
        strcpy(name,input_name);  
    }  
    Teacher()//無參數(shù)構造函數(shù),進行函數(shù)重載  
    {  
      
    }  
    void show();  
  
    protected:  
    char *name;  
  
};  
void Teacher::show()  
{  
    cout<<name<<endl;  
}  
void main()  
{  
    Teacher test;  
    Teacher a("test");  
    a.show();  
    cin.get();  
}
  • 創(chuàng)建一個無參數(shù)的同名的 Teacher()無參數(shù)函數(shù),一重載方式區(qū)分調用,由于構造函數(shù)和普通函數(shù)一樣具有重載特性所以編寫程序的人可以給一個類添加任意多個構造函數(shù),來使用不同的參數(shù)來進行初始化對象。
  • C++規(guī)定如果一個類對象是另外一個類的數(shù)據成員,那么在創(chuàng)建對象的時候系統(tǒng)將自動調用哪個類的構造函數(shù)。
#include <iostream>  
using namespace std;    
class Teacher  
{  
    public:  
    Teacher()  
    {  
        director = new char[10];  
        strcpy(director,"王大力");  
    }  
    char * show();  
    protected:  
    char * director;  
};  
char *Teacher::show()  
{  
    return director;  
}  
class Student  
{  
    public:  
    Student()  
    {  
        number = 1;  
        score = 100;  
    }  
    void show();  
    protected:  
    int number;  
    int score;  
    Teacher teacher;//這個類的成員teacher是用Teacher類進行創(chuàng)建并初始化的  
};  
void Student::show()  
{  
    cout<<teacher.show()<<endl<<number<<endl<<score<<endl;  
}  
void main()  
{  
    Student a;  
    a.show();  
    Student b[3];  
    for(int i=0; i<sizeof(b)/sizeof(Student); i++)  
    {  
        b[i].show();  
    }  
    cin.get();  
}
  • 上面代碼中的Student類成員中teacher成員是的定義是用類Teacher進行定義創(chuàng)建的,那么系統(tǒng)碰到創(chuàng)建代碼的時候就會自動調用Teacher類中的Teacher()構造函數(shù)對對象進行初始化工作!
  • 這個例子說明類的分工很明確,只有碰到自己的對象的創(chuàng)建的時候才自己調用自己的構造函數(shù)。

二、析構函數(shù)

  • 一個類可能需要在構造函數(shù)內動態(tài)分配資源,那么這些動態(tài)開辟的資源就需要在對象不復存在之前被銷毀掉,那么c++類的析構函數(shù)就提供了這個方便。

  • 析構函數(shù)的定義:**析構函數(shù)也是特殊的類成員函數(shù),它沒有返回類型,沒有參數(shù),不能隨意調用,也沒有重載,只有在類對象的生命期結束的時候,由系統(tǒng)自動調用。 **

  • 析構函數(shù)與構造函數(shù)的不同:析構函數(shù)與構造函數(shù)最主要大不同就是在于調用期不同,構造函數(shù)可以有參數(shù)可以重載!

  • 如何編寫析構函數(shù):

    • 析構函數(shù)可以的特性是在程序結束的時候逐一調用,那么正好與構造函數(shù)的情況是相反,屬于互逆特性,所以定義析構函數(shù)因使用“~”符號(邏輯非運算符),標示它為逆構造函數(shù),加上類名稱來定義。
#include <iostream>  
#include <string>  
using namespace std;    
class Teacher  
{  
    public:  
    Teacher()  
    {  
        director = new char[10];  
        strcpy(director,"王大力");  
        //director = new string;  
        // *director="王大力";//string情況賦值  
    }  
    ~Teacher()  
    {  
        cout<<"釋放堆區(qū)director內存空間1次";  
        delete[] director;  
        cin.get();  
    }  
    char *show();  
    protected:  
    char *director;  
    //string *director;  
};  
char *Teacher::show()  
{  
    return director;  
}  
class Student  
{  
    public:  
    Student()  
    {  
        number = 1;  
        score = 100;  
    }  
    void show();  
    protected:  
    int number;  
    int score;  
    Teacher teacher;  
};  
void Student::show()  
{  
    cout<<teacher.show()<<endl<<number<<endl<<score<<endl;  
}  
void main()  
{  
    Student a;  
    a.show();  
    Student b[3];  
    for(int i=0; i<sizeof(b)/sizeof(Student); i++)  
    {  
        b[i].show();  
    }  
    cin.get();  
}
  • 上面的代碼中我們?yōu)門eacher類添加了一個名為~Teacher()的析構函數(shù)用于清空堆內存。
  • 程序將在結束前也就是對象聲明周期結束的時候自動調用~Teacher()。
  • 有直接的關系,以后能為 delete 操作符只能清空堆空間而不能夠清除??臻g,如果強行清除??丶却娴漠媽е鲁绦虻谋罎ⅲ?/li>
#include <iostream>    
#include <string>    
using namespace std;      
class Teacher    
{    
    public:    
    Teacher(char *temp)    
    {    
        director = new char[10];    
        strcpy(director,temp);  
    }  
    ~Teacher()    
    {    
        cout<<"釋放堆區(qū)director內存空間1次";    
        delete[] director;    
        cin.get();    
    }    
    char *show();    
    protected:    
    char *director;    
};    
char *Teacher::show()    
{    
    return director;    
}    
class Student    
{    
    public:    
    Student()    
    {    
        number = 1;    
        score = 100;    
    }    
    void show();    
    protected:    
    int number;    
    int score;    
    Teacher teacher("王大力");//錯誤,一個類的成員如果是另外一個類的對象的話,不能在類中使用帶參數(shù)的構造函數(shù)進行初始化      
};    
void Student::show()    
{    
    cout<<teacher.show()<<endl<<number<<endl<<score<<endl;    
}    
void main()    
{    
    Student a;    
    a.show();    
    Student b[3];    
    for(int i=0; i<sizeof(b)/sizeof(Student); i++)    
    {    
        b[i].show();    
    }    
    cin.get();    
}

這樣不行,程序不能夠編譯成功,
因為:類是一個抽象的概念,并不是一個實體,并不能包含屬性值,只有對象才占有一定的內存空間,含有明確的屬性值。

  • C++的解決方案是叫構造類成員。
#include <iostream>    
using namespace std;      
class Teacher    
{    
    public:    
    Teacher(char *temp)    
    {    
        director = new char[10];    
        strcpy(director,temp);    
    }  
    ~Teacher()    
    {    
        cout<<"釋放堆區(qū)director內存空間1次";    
        delete[] director;    
        cin.get();    
    }    
    char *show();    
    protected:    
    char *director;    
};    
char *Teacher::show()    
{    
    return director;    
}    
class Student    
{    
    public:    
    Student(char *temp):teacher(temp)  
    {    
        number = 1;    
        score = 100;    
    }    
    void show();    
    protected:    
    int number;    
    int score;    
    Teacher teacher;    
};    
void Student::show()    
{    
    cout<<teacher.show()<<endl<<number<<endl<<score<<endl;    
}    
void main()    
{    
    Student a("王大力");    
    a.show();    
    //Student b[3]("王大力");  //這里這么用是不對的,數(shù)組不能夠使用帶參數(shù)的構造函數(shù),以后我們將詳細介紹vector類型  
    // for(int i=0; i<sizeof(b)/sizeof(Student); i++)    
    //{    
    //    b[i].show();    
    //}    
    cin.get();    
}
  • 最明顯的改變在這里
Student(char *temp):teacher(temp) 

冒號后的 teacher 就是要告訴調用 Student 類的構造函數(shù)的時候把參數(shù)傳遞給成員 teacher 的 Teacher 類的構造函數(shù),這樣一來我們就成功的在類體外對 teacher 成員進行了初始化,既方便也搞笑,這種冒號后制定調用某成員構造函數(shù)的方式,可以同時制定多個成員,這一特性使用都好方式,例如:

Student(char *temp):teacher(temp),abc(temp),def(temp) 

修改一下代碼:

#include <iostream>    
#include <string>    
using namespace std;      
class Teacher    
{    
    public:    
    Teacher(char *temp)    
    {    
        director = new char[10];    
        strcpy(director,temp);    
    }  
    ~Teacher()    
    {    
        cout<<"釋放堆區(qū)director內存空間1次";    
        delete[] director;    
        cin.get();  
    }    
    char *show();    
    protected:    
    char *director;    
};    
char *Teacher::show()    
{    
    return director;    
}    
class Student    
{    
    public:    
    Student(char *temp,int &pk):teacher(temp),pk(pk),ps(10)  
    {    
        number = 1;    
        score = 100;  
    }    
    void show();    
    protected:    
    int number;    
    int score;    
    Teacher teacher;  
    int &pk;  
    const int ps;  
};    
void Student::show()    
{    
    cout<<teacher.show()<<endl<<number<<endl<<score<<endl<<pk<<endl<<ps<<endl;    
}    
void main()    
{    
    char *t_name="王大力";  
    int b=99;  
    Student a(t_name,b);  
    a.show();  
    cin.get();  
}
  • 調用的時候我們使用 :
Student a(t_name,b); 
  • 我們將b的地址傳遞給了int &pk這個引用,使得Student類的引用成員pk和常量成員ps進行了成功的初始化。
    但是細心的人會發(fā)現(xiàn),我們在這里使用的初始化方式并不是在構造函數(shù)內進行的,而是在外部進行初始化的,的確,在冒號后和在構造函數(shù)括號內的效果是一樣的, 但和teacher(temp)所不同的是,pk(pk)的括號不是調用函數(shù)的意思,而是賦值的意思,我想有些讀者可能不清楚新標準的 C++對變量的初始 化是允許使用括號方式的,int a=10和int a(10)的等價的,但冒號后是不允許使用=方式只允許()括號方式,所以這里只能使用pk(pk)而不能是pk=pk了。
    ***c++規(guī)定,所有的全局對象和全局變量一樣都在主函數(shù)main()之前被構造,函數(shù)體內的靜態(tài)對象則只構造一次,也就是說只在首次進入這個函數(shù)的時候進行構造! ***
#include <iostream>    
#include <string>    
using namespace std;      
class Test  
{  
public:  
    Test(int a)  
    {  
        kk=a;  
        cout<<"構造參數(shù)a:"<<a<<endl;  
    }  
public:  
    int kk;  
};  
void fun_t(int n)  
{  
    static Test a(n);  
    //static Test a=n;//這么寫也是對的  
    cout<<"函數(shù)傳入參數(shù)n:"<<n<<endl;  
    cout<<"對象a的屬性kk的值:"<<a.kk<<endl;  
}  
Test m(100);  
void main()  
{  
    fun_t(20);  
    fun_t(30);  
    cin.get();  
}
#include <iostream>    
#include <string>    
using namespace std;      
class Test  
{  
public:  
    Test(int a)  
    {  
        kk=a;  
        cout<<"構造參數(shù)a:"<<a<<endl;  
    }  
public:  
    int kk;  
};  
void fun_t(int n)  
{  
    static Test a(n);  
    //static Test a=n;//這么寫也是對的  
    cout<<"函數(shù)傳入參數(shù)n:"<<n<<endl;  
    cout<<"對象a的屬性kk的值:"<<a.kk<<endl;  
}  
Test m(100);  
void main()  
{  
    fun_t(20);  
    fun_t(30);  
    cin.get();  
}
#include <iostream>    
using namespace std;      
  
class Test  
{  
public:  
    Test(int j):pb(j),pa(pb+5)  
    {  
          
    }  
public:  
    int pa;  
    int pb;  
};  
void main()  
{  
    Test a(10);  
    cout<<a.pa<<endl;  
    cout<<a.pb<<endl;  
    cin.get();  
}

類成員的構造是按照在類中定義的順序進行的,而不是按照構造函數(shù)說明后的冒號順序進行構造的。

三、Q&A

  • delete 某個類的指針會調用該類的析構函數(shù)么?
    會的.如果不調用的話怎么析構一個類.
    不過指針所指向的對象必須是在堆中用new關鍵詞開創(chuàng)的
    如果指針指向的是一個棧中的對象,會引起調用兩種析構函數(shù)而導致程序錯誤
class ob
ob a;
ob *p=&a;
delete p;//這樣會導致調用兩次析構函數(shù).是會引起程序錯誤的

只有

class ob
ob * p= new ob;
delete p; //這樣是正確的
  • 對于 new 創(chuàng)建的對象,只有調用 delete 才能析構。

  • 構造函數(shù):

    • 什么是構造函數(shù)?通俗的講,在類中,函數(shù)名和類名相同的函數(shù)稱為構造函數(shù)。它的作用是在建立一個對象時,作某些初始化的工作(例如對數(shù)據賦予初值)。C++允許同名函數(shù),也就允許在一個類中有多個構造函數(shù)。如果一個都沒有,編譯器將為該類產生一個默認的構造函數(shù)。
      構造函數(shù)上惟一的語法限制是它不能指定返回類型,甚至void 也不行。
      不帶參數(shù)的構造函數(shù):一般形式為 類名 對象名(){函數(shù)體}
      帶參數(shù)的構造函數(shù):不帶參數(shù)的構造函數(shù),只能以固定不變的值初始化對象。帶參數(shù)構造函數(shù)的初始化要靈活的多,通過傳遞給構造函數(shù)的參數(shù),可以賦予對象不同的初始值。一般形式為:構造函數(shù)名(形參表);
      創(chuàng)建對象使用時:類名 對象名(實參表);
      構造函數(shù)參數(shù)的初始值:構造函數(shù)的參數(shù)可以有缺省值。當定義對象時,如果不給出參數(shù),就自動把相應的缺省參數(shù)值賦給對象。一般形式為:
      構造函數(shù)名(參數(shù)=缺省值,參數(shù)=缺省值,……);
  • 析構函數(shù):

    • 當一個類的對象離開作用域時,析構函數(shù)將被調用(系統(tǒng)自動調用)。析構函數(shù)的名字和類名一樣,不過要在前面加上 ~ 。對一個類來說,只能允許一個析構函數(shù),析構函數(shù)不能有參數(shù),并且也沒有返回值。析構函數(shù)的作用是完成一個清理工作,如釋放從堆中分配的內存。
      一個類中可以有多個構造函數(shù),但析構函數(shù)只能有一個。對象被析構的順序,與其建立時的順序相反,即后構造的對象先析構。
最后編輯于
?著作權歸作者所有,轉載或內容合作請聯(lián)系作者
【社區(qū)內容提示】社區(qū)部分內容疑似由AI輔助生成,瀏覽時請結合常識與多方信息審慎甄別。
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發(fā)布,文章內容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務。

相關閱讀更多精彩內容

友情鏈接更多精彩內容