1. 程式人生 > >C++: 建構函式 & 拷貝建構函式 & 解構函式 & explicit

C++: 建構函式 & 拷貝建構函式 & 解構函式 & explicit

一、建構函式

1.定義

建構函式是類的成員函式,函式名與類名相同,在建立類型別物件時,由編譯器自動呼叫,一個物件在被建立時,會呼叫一次建構函式,此後,不會在呼叫建構函式,即就是建構函式是在物件的整個生命週期裡只會被呼叫一次

2.建構函式的書寫格式

這裡寫圖片描述

3.建構函式的特性

(1)建構函式的函式名與類名相同;
(2)建構函式有初始化列表;
(3)建構函式由編譯器自動呼叫;
(4)建構函式的呼叫時機:

在物件建立時才會被呼叫,並且在物件的整個生命週期中只存在一次;

(5)建構函式沒有返回值,但是有引數;

ps:其實建構函式是有返回值的,返回的是當前物件的地址

(6)建構函式沒有顯式定義時,編譯器會提供一個預設的建構函式

ps:編譯器合成預設建構函式有四個場景,具體參考部落格:

(7)建構函式不能用const修飾;

const在修飾函式時,實際是在修飾隱含的this指標,表示在此函式中不可以對類的任何成員進行修改,而構函式恰恰是用來構造物件的,所以建構函式不能用const修飾;

(8)建構函式可以過載,實參決定了呼叫那個建構函式;
(9)無參建構函式和帶有預設值(此處是全預設)的建構函式都認為是預設建構函式,並且預設的建構函式只能有一個,即就是無參的建構函式和帶有預設值的建構函式只能存在一個;
(10)建構函式不能為虛擬函式

ps:無參建構函式

class Stu
{
public:
    Stu()
    {}
private:
    int _id;
    int _grade;
    char _name[5];
};

帶有預設值的建構函式

這裡寫圖片描述

4.建構函式的作用

(1)初始化物件;
(2)構建物件;
(3)型別轉換(此時的建構函式一定要是單參的,也可以是多個引數,只要帶著預設值就好)

5.初始化列表

(1)初始化列表的格式

以一個冒號開始,接著是一個逗號分隔的資料成員列表,每個資料後面圓括號中的值就是該變數的初始值,如上圖 ,就是一個初始化列表

(2)作用

是用來初始化物件的

(3)特性

a.每個資料成員在初始化列表裡只能出現一次;
b.初始化列表只是用來初始化成員的,並不指定這些成員的初始化順序,資料成員在類中的定義順序才是引數列表的初始化順序
ps:在初始化變數時,儘量與變數的宣告順序一致,避免造成不必要的錯誤

(4)類中以下成員必須要在初始化列表中定義:

a、引用資料成員
因為引用型別的變數必須初始化,而如果在函式體中才開始初始化的話,此時就是賦值,不是初始化了,所以對引用成員必須放在初始化列表中初始化
b、const修飾的資料成員
c、類型別資料成員

6.關鍵字explicit

(1)用此關鍵字修飾建構函式的話,會抑制由建構函式定義的隱式轉換
(2)explicit使用位置
當建立類成員變數時,加上此關鍵字就可以了,在類外就不需要在重複了

二、拷貝建構函式

1.定義

此函式只有單個形參,而且該引數的型別是對本類型別的引用(常用const修飾)
ps:拷貝建構函式是特殊的建構函式,建立物件時使用已經存在的同類物件來進行初始化

2.拷貝建構函式的格式

這裡寫圖片描述

#define _CRT_SECURE_NO_WARNINGS
#include<iostream>
#include<string>
using namespace std;
class Stu
{
public:
    Stu(int id=10, int grade=100,char*name="張三")
        :_id(id)
        , _grade(grade)
    {
        strcpy(_name, name);
        cout << _id << "," << _grade << "," << _name << endl;
    }
    Stu(const Stu&s)
    {
        _id = s._id;
        _grade = s._grade;
        strcpy(_name, s._name);
    }
private:
    int _id;
    int _grade;
    char _name[5];
};
int main()
{
    Stu s1(1, 99,"李四");
    Stu s2(2);
    Stu s3;
    Stu s4(s1);
    system("pause");
    return 0;
}

3.拷貝建構函式的呼叫時機

在構建物件時,由編譯器自動呼叫

4.拷貝建構函式的引數型別為什麼是引用?

這裡寫圖片描述
ps:如果不傳引用,程式會崩潰,導致棧溢位

5.拷貝建構函式的特性

(1)由編譯器自動呼叫;
(2)是建構函式的過載,所以拷貝建構函式的函式名與類名相同;
(3)只有單個形參,且此引數是類型別的引用,常用const修飾;
(4)建構函式的特性,拷貝建構函式都有,只有一個特性拷貝建構函式是沒有的,就是建構函式可以過載,拷貝建構函式可以過載;
(5)拷貝建構函式不能過載;
(6)如果沒有顯式定義,系統會自動合成一個預設的拷貝構造暗黑少女胡,合成的拷貝建構函式會依次拷貝類的資料成員,完成相關物件的初始化;
ps:系統合成預設的拷貝建構函式是有具體的場景的(場景與系統自動合成預設建構函式的場景類似),並不是什麼時候都會合成。

6.使用場景

(1)物件例項化物件

#define _CRT_SECURE_NO_WARNINGS
#include<iostream>
#include<string>
using namespace std;
class Stu
{
public:
    Stu(int id=10, int grade=100,char*name="張三")
        :_id(id)
        , _grade(grade)
    {
        strcpy(_name, name);
        cout << _id << "," << _grade << "," << _name << endl;
    }
    Stu(const Stu&s)
    {
        _id = s._id;
        _grade = s._grade;
        strcpy(_name, s._name);
        cout << _id << "," << _grade << "," << _name << endl;
    }
private:
    int _id;
    int _grade;
    char _name[5];
};
int main()
{
    Stu s1(1, 99,"李四");
    Stu s2;
    Stu s3(s2);
    system("pause");
    return 0;
}

執行結果:這裡寫圖片描述

(2)傳值方式作為函式的引數

#define _CRT_SECURE_NO_WARNINGS
#include<iostream>
#include<string>
using namespace std;
class Stu
{
public:
    Stu(int id=10, int grade=100,char*name="張三")
        :_id(id)
        , _grade(grade)
    {
        strcpy(_name, name);
        cout << _id << "," << _grade << "," << _name << endl;
    }
    Stu(const Stu&s)
    {
        _id = s._id;
        _grade = s._grade;
        strcpy(_name, s._name);
    }
    void SetInfo(const Stu s){
    _id = s._id;
    _grade = s._grade;
    strcpy(_name, s._name);
    cout << _id << "," << _grade << "," << _name << endl;

    }
private:
    int _id;
    int _grade;
    char _name[5];
};
int main()
{
    Stu s1(1, 99,"李四");
    Stu s2;
    Stu s3(s2);
    Stu s4;
    s4.SetInfo(s1);
    system("pause");
    return 0;
}

執行結果:這裡寫圖片描述
(3)傳值方式作為函式的返回值

三、解構函式

1.定義

與建構函式相反,解構函式是用來銷燬物件的

2.解構函式的呼叫時機

在物件被銷燬時,由編譯器自動呼叫

3.解構函式的作用

銷燬物件,完成類的一些清理和汕尾工作

4.解構函式的格式

解構函式名與建構函式名相同,只不過在函式名之前多加了一個符號:~

~Stu()
{
  需要銷燬的類的成員
}

5.解構函式的特性

(1)解構函式名=在類名之前多加一個字元:~
(2)解構函式無引數無返回值
(3)在物件生命週期結束時,解構函式由編譯器自動呼叫
(4)一個類只能有一個解構函式,若解構函式沒有顯式定義,系統會自動合成一個預設的解構函式

ps:只有當涉及到資源的清理的時候,系統才會合成一個解構函式
(5)解構函式不能被過載,因為解構函式沒有引數
(6)解構函式不是刪除物件,而是做一些資源清理工作