1. 程式人生 > >C++解析(12):初始化列表與物件構造順序

C++解析(12):初始化列表與物件構造順序

0.目錄

1.類成員的初始化

2.類中的const成員

3.物件的構造順序

4.小結

1.類成員的初始化

類中是否可以定義const成員?

下面的類定義是否合法?如果合法ci的值是什麼儲存在哪裡?

(會報錯)

C++中提供了初始化列表對成員變數進行初始化
語法規則:

注意事項:

  • 成員的初始化順序與成員的宣告順序相同
  • 成員的初始化順序與初始化列表中的位置無關
  • 初始化列表先於建構函式的函式體執行

證明成員的初始化順序與成員的宣告順序相同,而與初始化列表中的位置無關:

#include <stdio.h>

class Value
{
private:
    int mi;
public:
    Value(int i)
    {
        printf("i = %d\n", i);
        mi = i;
    }
    int getI() { return mi; }
};

class Test
{
private:
    Value m2;
    Value m3;
    Value m1;
public:
    Test() : m1(1), m2(2), m3(3)
    {
        printf("Test::Test()\n");
    }
};


int main()
{
    Test t;
    
    return 0;
}

執行結果為:

[[email protected] Desktop]# g++ test.cpp
[[email protected] Desktop]# ./a.out 
i = 2
i = 3
i = 1
Test::Test()

2.類中的const成員

類中的const成員:

  • 類中的const成員會被分配空間
  • 類中的const成員的本質是隻讀變數
  • 類中的const成員只能在初始化列表中指定初始化

編譯器無法直接得到const成員的初始值,因此無法進入符號表成為真正意義上的常量。

證明類中的const成員的本質是隻讀變數:

#include <stdio.h>

class Value
{
private:
    int mi;
public:
    Value(int i)
    {
        printf("i = %d\n", i);
        mi = i;
    }
    int getI()
    {
        return mi;
    }
};

class Test
{
private:
    const int ci;
    Value m2;
    Value m3;
    Value m1;
public:
    Test() : m1(1), m2(2), m3(3), ci(100)
    {
        printf("Test::Test()\n");
    }
    int getCI()
    {
        return ci;
    }
    int setCI(int v)
    {
        int* p = const_cast<int*>(&ci);
        
        *p = v;
    }
};


int main()
{
    Test t;
    
    printf("t.ci = %d\n", t.getCI());
    
    t.setCI(10);
    
    printf("t.ci = %d\n", t.getCI());
    
    return 0;
}

執行結果為:

[[email protected] Desktop]# g++ test.cpp
[[email protected] Desktop]# ./a.out 
i = 2
i = 3
i = 1
Test::Test()
t.ci = 100
t.ci = 10

初始化與賦值不同:

  • 初始化:對正在建立的物件進行初值設定
  • 賦值:對已經存在的物件進行值設定

3.物件的構造順序

3.1 區域性物件的構造順序

對於區域性物件——當程式執行流到達物件的定義語句時進行構造

下面程式中的物件構造順序是什麼?

示例:

#include <stdio.h>

class Test
{
private:
    int mi;
public:
    Test(int i)
    {
        mi = i;
        printf("Test(int i): %d\n", mi);
    }
    Test(const Test& obj)
    {
        mi = obj.mi;
        printf("Test(const Test& obj): %d\n", mi);
    }
};

int main()
{
    int i = 0;
    Test a1 = i;
        
    while( i < 3 )
    {
        Test a2 = ++i;
    }
        
    if( i < 4 )
    {
        Test a = a1;
    }
    else
    {
        Test a(100);
    }

    return 0;
}

執行結果為:

[[email protected] Desktop]# g++ test.cpp
[[email protected] Desktop]# ./a.out 
Test(int i): 0
Test(int i): 1
Test(int i): 2
Test(int i): 3
Test(const Test& obj): 0

以下程式改變了程式的執行流,有嚴重的bug,但是不同的編譯器有不同的處理結果,有些編譯器不會報錯,一定要注意這種問題!

#include <stdio.h>

class Test
{
private:
    int mi;
public:
    Test(int i)
    {
        mi = i;
        printf("Test(int i): %d\n", mi);
    }
    Test(const Test& obj)
    {
        mi = obj.mi;
        printf("Test(const Test& obj): %d\n", mi);
    }
    int getMi()
    {
        return mi;
    }
};

int main()
{
    int i = 0;
    Test a1 = i; // Test(int i): 0
        
    while( i < 3 )
    {
        Test a2 = ++i; // Test(int i): 1, 2, 3
    }
goto End;       
        Test a(100);
End:
    printf("a.mi = %d\n", a.getMi());
    return 0;
}

3.2 堆物件的構造順序

對於堆物件

  • 當程式執行流到達new語句時建立物件
  • 使用new建立物件將自動觸發建構函式的呼叫

下面程式中的物件構造順序是什麼?

示例:

#include <stdio.h>

class Test
{
private:
    int mi;
public:
    Test(int i)
    {
        mi = i;
        printf("Test(int i): %d\n", mi);
    }
    Test(const Test& obj)
    {
        mi = obj.mi;
        printf("Test(const Test& obj): %d\n", mi);
    }
    int getMi()
    {
        return mi;
    }
};

int main()
{
    int i = 0;
    Test* a1 = new Test(i); // Test(int i): 0
        
    while( ++i < 10 )
        if( i % 2 )
            new Test(i); // Test(int i): 1, 3, 5, 7, 9
        
    if( i < 4 )
        new Test(*a1);
    else
        new Test(100); // Test(int i): 100
        
    return 0;
}

執行結果為:

[[email protected] Desktop]# g++ test.cpp
[[email protected] Desktop]# ./a.out 
Test(int i): 0
Test(int i): 1
Test(int i): 3
Test(int i): 5
Test(int i): 7
Test(int i): 9
Test(int i): 100

3.3 全域性物件的構造順序

對於全域性物件

  • 物件的構造順序是不確定的
  • 不同的編譯器使用不同的規則確定構造順序

(要避開全域性物件之間的相互依賴!)

4.小結

  • 類中可以使用初始化列表對成員進行初始化
  • 初始化列表先於建構函式體執行
  • 類中可以定義const成員變數
  • const成員變數必須在初始化列表中指定初值
  • const成員變數為只讀變數
  • 區域性物件的構造順序依賴於程式的執行流
  • 堆物件的構造順序依賴於new的使用順序
  • 全域性物件的構造順序是不確定的