經典問題解析二(十九)
下來我們以代碼為例進行說明
#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。
經典問題解析二(十九)