【C++】 21_對象的構造順序

問題 1:
C++ 中的類能夠定義多個對象,那麼對象構造的順序是怎樣的?編程

問題 2:
對象構造順序會帶來什麼影響呢?函數

對象構造每每與構造函數相關聯,構造函數體有多是很是複雜的程序邏輯組成,不一樣類的構造函數中程序邏輯多是相互依賴的。當相互依賴發生時,對象的構造順序就變得尤其重要。

對象的構造順序 一

  • 對於局部對象(包含靜態局部對象)code

    • 當程序執行流到達對象的定義語句時進行構造

下面程序中的對象構造順序是什麼?對象

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

實例分析: 局部對象的構造順序

test_1.cppget

#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("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;
}
輸出:
Test(int i) : 0
Test(int i) : 1
Test(int i) : 2
Test(int i) : 3
const Test& obj : 0

結論:
局部對象的構造順序與程序的執行流相關

error.cpp編譯器

#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("const Test& obj : %d\n", mi);
    }
    int getMi()
    {
        return mi;
    }
};

int main()
{
    int i = 0;
    Test a1 = i;
    
    while( i < 3)
        Test a2 = ++i;
        
goto End;                // 注意這裏!
        Test a(100);
End:

    printf("a.mi = %d\n", a.getMi());

    return 0;
}
g++ 輸出:
test.cpp:30: error: jump to label ‘End’
test.cpp:28: error:   from here
test.cpp:29: error:   crosses initialization of ‘Test a’


vc2010 編譯輸出:
error.cpp(34) : warning C4533: 「goto a」跳過了「End」的初始化操做
error.cpp(33) : 參見「a」的聲明
error.cpp(34) : 參見「End」的聲明

vc2010 運行輸出:
Test(int i) : 0
Test(int i) : 1
Test(int i) : 2
Test(int i) : 3
a.mi = 4076341

發生了什麼?it

  1. 程序的執行流被改變,意味着對象 a 的狀態是沒有被初始化的(構造函數沒有被調用)。那麼以後使用該對象的結果將是不肯定的,可能致使嚴重的災難!!因此當定義使用對象時,要對程序的執行流有清晰的認識。
  2. g++給出錯誤提示,不能夠保證全部的編譯器都有這樣的實現,由於這不是C++的標準。VC2010未給出錯誤提示,僅給出警告。

對象的構造順序 二

  • 對於堆對象io

    • 當程序執行流到達 new 語句時建立對象
    • 使用 new 建立對象將自動觸發構造函數的調用

下面程序中的對象構造順序是什麼?編譯

void code()
{
    int i = 0;
    Test* a1 = new Test(i);
    
    while( ++i < 10 )
        if( i % 2 )
            new Test(i);
            
    if( i < 4 )
        new Test(*a1);
    else
        new Test(100);
    
    return 0;
}

編程實驗: 堆對象的構造順序

#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("const Test& obj : %d\n", mi);
    }
    
    int getMi()
    {
        return mi;
    }
};

// 這裏在 new 以後沒有 delete,僅爲演示
int main()
{
    int i = 0;
    Test* a1 = new Test(i);
    
    while( ++i < 10 )
        if( i % 2 )
            new Test(i);
            
    if( i < 4 )
        new Test(*a1);
    else
        new Test(100);
    
    return 0;
}
輸出:
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

結論:
堆對象的構造順序與程序的執行流相關。堆對象一樣受到程序執行流的影響,具體可參考局部對象中的分析。

對象的構造順序 三

  • 對於全局對象class

    • 對象的構造順序是不肯定的
    • 不一樣的編譯器使用不一樣的規則肯定構造順序

實例分析: 全局對象的構造順序

test.h

#ifndef _TEST_H_
#define _TEST_H_

#include <stdio.h>

class Test
{
public:
    Test(const char* s)
    {
        printf("%s\n", s);
    }
};

#endif

t1.cpp

#include "test.h"

Test t1("t1");

t2.cpp

#include "test.h"

Test t2("t2");

t3.cpp

#include "test.h"

Test t3("t3");

test.cpp

#include "test.h"

Test t4("t4");

int main()
{
    Test t5("t5");
    
    return 0;
}
g++ 輸出:
t3
t2
t1
t4
t5

vc2010 輸出:
t4
t1
t2
t3
t5

警示: 儘可能避開全局對象,儘可能避開全局對象的相互依賴

小結

  • 局部對象的構造順序依賴於程序的執行流
  • 堆對象的構造順序依賴於 new 的使用順序
  • 全局對象的構造順序是不肯定的

以上內容參考狄泰軟件學院系列課程,請你們保護原創!

相關文章
相關標籤/搜索