1. 程式人生 > >建構函式 解構函式 拷貝建構函式

建構函式 解構函式 拷貝建構函式

一、建構函式

為了更好的說明建構函式,首先建立一個簡單的日期類

Date.cpp

class Date
{
public:
    void DatePrint();
    void DateSet(int year, int month, int day);
private:
    int m_year;
    int m_month;
    int m_day;
 };

void Date::DateSet(int year, int month, int day)
{
    m_year = year;
    m_month = month;
    m_day = day;
}

void Date::DatePrint()
{
    cout << m_year << "-" << m_month << "-" << m_day << endl;
}

1.成員變數命名風格

看到一些程式碼我們可能看的一臉僵硬,例如:

void Date::DateSet(int year, int month, int day)
{
    year = year;
    month = month;
    day = day;
}

這裡的 year, month, day 到底是成員變數呢,還是函式形參呢,所以在建立成員變數的時候,我們最好加上字首或字尾用來區分,例如:

void Date::DateSet(int year, int month, int day)
{
    m_year = year;
    m_month = month;
    m_day = day;
}

2. 概念

建構函式是一個特殊的成員函式,名字與類相同,建立型別物件時由編譯器自動呼叫,在類的生命週期內只調用一次

3. 特性

(1) 函式名與類名相同

(2) 無返回值

(3) 物件例項化時編譯器自動呼叫對應的建構函式

(4) 建構函式可以過載

4. 無參建構函式

如果類中沒有顯式定義的建構函式,C++編譯器會自動的生成一個無參的建構函式,使用者顯式定義,編譯器將不再生成,也許有人會疑問無參的建構函式看起來沒什麼用,編譯器為什麼要生建構函式呢?我們建立一個 Time 類,程式碼如下:

class Date
{
public:
    void DatePrint();
    void DateSet(int year, int month, int day);
private:
    // 內建型別
    int m_year;
    int m_month;
    int m_day;
    // 自定義型別
    Time m_t;
};

class Time
{
public:
    Time();
private:
    int m_hour;
    int m_minute;
    int m_second;
};

Time::Time()
{
    cout << "Time()" << endl;
    m_hour = 10;
    m_minute = 14;
    m_second = 15;
}

我們可以發現,對於上面的程式碼,編譯器生成的預設建構函式會對自定義型別成員 m_t 呼叫它的預設成員函式

另外,如果通過無參建構函式建立物件時,不用跟括號,否則就成了函式宣告,例如:

void DateTest()
{
    Date d1;
    Date d2();
}

d1 為呼叫無參建構函式,d2 為函式宣告,該函式無參,返回值為日期型別的物件

5. 帶參建構函式

Date::Date(int year , int month, int day)
{
    m_year = year;
    m_month = month;
    m_day = day;
}

呼叫帶參構函式

void DateTest()
{
    Date d3(2018, 11, 21);
}

二、解構函式

1. 概念

程式在物件在銷燬時會自動呼叫解構函式,完成類的一些資源清理工作

2. 特性

(1)  解構函式名是在類名前加上字元 ~

(2)  無引數無返回值

(3)  一個類有且只有一個解構函式,若未顯式定義,系統會自動生成預設的解構函式 

(4)  物件生命週期結束時,C++編譯系統系統自動呼叫解構函式

(5)  解構函式不可以過載

3. 編譯器生成的預設解構函式對會自定型別成員呼叫它的解構函式

class String
{
public:
    String(const char* str = "test");
    ~String();
private:
    char* m_str;
};

String::String(const char* str)
{
    m_str = (char*)malloc(strlen(str) + 1);
    strcpy(m_str, str);
}
String::~String()
{
    cout << "~String()" << endl;
    free(m_str);
}

class Person
{
private:
    String m_name;
    int m_age;
};

4. 一個類中如果涉及到了資源的管理,解構函式一定要給出

三、拷貝建構函式

1. 概念 

拷貝建構函式,它是由編譯器用來完成一些基於同一類的其他物件的構建及初始化。其唯一的引數(物件的引用)是不可變的(常用const型別

2. 特性 

(1) 拷貝建構函式是一種特殊的建構函式,是建構函式的一個過載形式

(2) 拷貝建構函式的引數只有一個且必須使用引用傳參,如果使用傳值方式傳遞,它將會引發無窮遞迴呼叫

(3)  若未顯示定義,系統生成預設拷貝建構函式,預設的拷貝建構函式物件按記憶體儲存按位元組序完成拷貝,這種拷貝我們叫做淺拷貝,或者值拷貝,這種拷貝只對物件中的資料成員進行簡單的賦值

來一段程式碼檢驗一下:

class Date
{
public:
    Date(int year = 2018, int month = 11, int day = 23);
    Date(const Date& d);
private:
    int m_year;
    int m_month;
    int m_day;
};

Date::Date(int year, int month, int day)
{
    m_year = year;
    m_month = month;
    m_day = day;
}

Date::Date(const Date& d)
{
    m_year = d.m_year;
    m_month = d.m_month;
    m_day = d.m_day;
}

3. 預設拷貝建構函式

程式碼說明:d2呼叫預設拷貝構造,d2和d1的值一樣

#include <iostream>
using namespace std;

class Date
{
public:
    Date(int year = 2018, int month = 11, int day = 23);
private:
    int m_year;
    int m_month;
    int m_day;
};

Date::Date(int year, int month, int day)
{
    m_year = year;
    m_month = month;
    m_day = day;
}

void Test()
{
    Date d1;
    Date d2(d1);
}

4. 一個類中如果涉及到了資源的管理,拷貝函式一定要顯式提供,否則會共用同一個空間,該空間被多次釋放,從而引起程式的崩潰,不信你看

class String
{
public:
    String(const char* str = "word");
    ~String();
private:
    char* _str;
};

String::String(const char* str)
{
    _str = (char*)malloc(strlen(str) + 1);
    strcpy(_str, str);
}

String::~String()
{
    cout << "~String()" << endl;
    free(_str);
}

void test()
{
    String s1("hello");
    String s2(s1);
}