1. 程式人生 > >經典問題解析二(十九)

經典問題解析二(十九)

C++ 構造順序 const 成員函數 const 對象 this 指針

今天我們來探討下當程序中存在多個對象時,如何確定這些對象的析構順序?那麽單個對象創建時構造函數的調用順序是:a> 調用父類的構造過程(我們會在後面進行講解);b> 調用成員變量的構造函數(調用順序與生命順序相同);c> 調用類自身的構造函數。析構函數與對應構造函數的調用順序相反。當多個對象析構時,析構順序與構造順序相反。

下來我們以代碼為例進行說明

#include <stdio.h>

class Member
{
    const char* ms;
public:
    Member(const char* s)
    {
        printf("Member(const char* s): %s\n", s);
        
        ms = s;
    }
    ~Member()
    {
        printf("~Member(): %s\n", ms);
    }
};

class Test
{
    Member mB;
    Member mA;
public:
    Test() : mA("mA"), mB("mB")
    {
        printf("Test()\n");
    }
    ~Test()
    {
        printf("~Test()\n");
    }
};

Member gA("gA")

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

我們先來分析下,首先我們定義兩個類 Member 和 Test,在類 Test 中定義了兩個類成員 Member。當程序流往下執行時,首先會創建全局類 Member gA,下來是類 Member mB,mA;註意這塊它們的順序和聲明的順序是相同的。最後才是創建類 Test 本身,後面析構的順序和構造順序是相反的,我們來看看編譯結果

技術分享圖片

我們看到結果和我們分析的是一致的。那麽對於棧對象和全局對象,便類似於入棧與出棧的順序,最後構造的對象是最先被析構的。堆對象的析構發生在使用 delete 的時候,與 delete 的使用順序相關!!

我們之前學習了 const 關鍵字,它可以修飾變量及函數。那麽它是否可以修飾類的對象呢?如果可以的話,有何特性呢?const 關鍵字是可以修飾對象的,它修飾的對象為只讀對象。只讀對象的成員變量不允許被改變,只讀對象是編譯階段的概念,運行時無效。我們下來來講下 C++ 中的 const 成員函數:a> const 對象只能調用 const 成員函數;b> const 成員函數中只能調用 const 成員函數;c> const 成員函數中不能直接改寫成員變量的值。const 成員函數的定義:Type ClassName::function(Type p) const。類中的函數聲明與實際函數定義中都必須帶 const 關鍵字。

下來我們還是以示例代碼進行分析說明

#include <stdio.h>

class Test
{
    int mi;
public:
    Test(int i);
    int getMI();
};

Test::Test(int i)
{
    mi = i;
}

int Test::getMI()
{
    return mi;
}

int main()
{
    const Test t(1);

    printf("t.getMI() = %d\n", t.getMI());
        
    return 0;
}

我們將 main 函數中的 Test 對象定義為 const 的,然後輸出 mi 的值,我們直接編譯下,看看能否編譯通過呢

技術分享圖片

我們看到直接報錯了,它說這是個 const 類型的對象。我們前面講過,const 類型的對象只能調用 const 成員函數,所以我們在 getMI 函數的聲明和實現中都加上 const,再來編譯看看

技術分享圖片

我們看到編譯通過,並且完美運行。那麽我們說過 const 成員函數是在其中不能改變值的,我們試試在 getMI 函數中賦值 mi 為 2,看看編譯是否通過呢技術分享圖片

它說這個成員在只讀的函數中,不能被改變。那麽成員函數和成員變量都隸屬於具體對象的嗎?從面向對象的角度,對象由屬性(成員變量)和方法(成員函數)構成。從程序運行的角度,對象由數據和函數構成,數據可以位於棧,堆和全局數據區,但函數只能位於代碼段。結論便是:a> 每一個對象擁有自己獨立的屬性(成員變量);b> 所有的對象共享類的方法(成員函數);c> 方法能夠直接訪問對象的屬性;d> 方法中的隱藏參數 this 用於指代當前對象。

我們以代碼為例進行說明

#include <stdio.h>

class Test
{
    int mi;
public:
    int mj;
    Test(int i);
    Test(const Test& t);
    int getMI() const;
    void print();
};

Test::Test(int i)
{
    mi = i;
}

Test::Test(const Test& t)
{
    mi = t.mi;
}

int Test::getMI() const
{
    return mi;
}

void Test::print()
{
    printf("this = %p\n", this);
}

int main()
{
    Test t1(1);
    Test t2(2);
    Test t3(3);
    
    printf("t1.getMi() = %d\n", t1.getMI());
    printf("&t1 = %p\n", &t1);
    t1.print();
    
    printf("t2.getMi() = %d\n", t2.getMI());
    printf("&t2 = %p\n", &t2);
    t2.print();
    
    printf("t3.getMi() = %d\n", t3.getMI());
    printf("&t3 = %p\n", &t3);
    t3.print();
    
    return 0;
}

我們定義了三個對象,分別打印出它們的 mi 的值,它們本身的地址以及對應的 this 指針的地址,看看編譯結果

技術分享圖片

那麽它們為什麽能知道自己定義的 mi 的值對應的是多少呢?就是因為每個對象有個隱藏的 this 指針,而且通過打印可以看出這個 this 指針和他們本身的地址是相同的。可能有的小夥伴對拷貝構造函數中的 mi 的賦值有疑問,可以這樣直接進行賦值嘛?這個 t.mi 不是私有成員嘛?通過運行,編譯器告訴了我們答案,是可以這樣寫的。因為成員函數是位於代碼段的,因此成員函數是唯一的。也就是說,成員函數只有一套,它能夠直接訪問對應類的成員變量。所以這塊是不沖突的。通過今天對特定問題的學習,總結如下:1、對象的析構順序與構造順序相反;2、const 關鍵字能夠修飾對象,得到的是只讀對象,只讀對象只能調用 const 成員函數;3、所有對象共享類的成員函數;5、隱藏的 this 指針用於表示當前對象。


歡迎大家一起來學習 C++ 語言,可以加我QQ:243343083

經典問題解析二(十九)