還是得從最近乙個比較「詭異」的問題說起:c++ 類指標定義的時候沒有初始化的時候,居然可以安全的呼叫類內部的成員函式而不出錯。
這段**是**於之前專案,然後我把問題抽離出來,另開乙個工程測試的時候,還是這個結果,最開始以為是vc編譯器的問題,最後跑到mac下用gcc編譯的結果依舊,說明這不是偶然現象,此時我才開始真正思考背後的緣由。在不斷的測試得出的結論是:
初始化為null的類指標可以安全的呼叫不涉及類成員變數的類成員函式而不出錯,但是如果類成員函式中呼叫了類成員變數則會出錯,既然賦值為null的情況都可以使用,那麼自然不初始化的類指標同樣滿足這類情況。
查了一些資料,未免自己理解有誤,特意將問題在群裡共享了一下,果然大家集思廣益的效率更高,最終問題得以解釋,同時也加深了自己對c++某些機制的理解,不說廢話,進入正題:
假設現在有乙個簡單的類定義如下:
class test
int get()
test():a(1),b(2){}
public:
int a,b;
};而之後編譯器會自動將這個類轉換成:
class test
;void _test_func(test * this);
int _test_get(test* this);
........
類中的函式被編譯器靜態編譯了,所有非虛函式(虛函式呢?別急,待會會解釋到)
都可以呼叫,因為函式位址編譯期間已經確定。我們知道,類中的成員函式都是通過this指標呼叫成員變數的,編譯器會將this指標作為預設引數傳給類成員函式的,如myclass.function(int a,int b) --> function(&myclass,int a,int b)
好,現在我們新增main函式如下:
int main()
執行結果見上面注釋,沒有呼叫成員變數的func()函式正確執行,呼叫了成員變數的get()函式錯誤。兩者其實都傳入了空的this指標,前者沒出錯僅僅是因為沒有呼叫this指標,而後者呼叫了。(此時p-func()和p->get()等同於func(null),get(null)......)是物件指標為null,而呼叫成員函式的時候,函式位址是編譯期間確定的,成員函式不通過物件指標(也即當前的p指標)去呼叫,物件指標僅僅作為引數傳入函式然後去呼叫成員變數。
好現在問題來了,如果是虛函式呢,
因為虛函式要通過this指標計算vptr,然後找到vtable,然後dispatch。因為this指標為空,所以在找vtable時候就會coredump了。總之這類情況下,一切呼叫了this指標的函式都會出錯,而完全不呼叫this指標的成員函式則沒問題。
gdb跟蹤截圖如下:
可以看到,最開始的時候p是有值的,不過是隨便指向的,*p指向,可以看出這個時候p指向的內容是隨機的不可控制的,然後往下走,p=null,*p對應0x0,但是呼叫p->func()的時候是通過$test()::func()呼叫的,這也進一步驗證了,之前說的,成員函式在靜態編譯的時候位址已經確定,呼叫的時候直接通過函式位址呼叫,this指標只是引數傳入,p->get()也一樣,只不過其內部呼叫了空的this指標來呼叫成員變數,所以出錯。
總結:任何時候定義指標的時候一定要初始化,這是良好的習慣,這個問題最初是由於當時寫程式疏忽造成的,然而錯有錯著居然編譯通過,所以當時一直沒發現這個手誤,現在追本溯源也算是對c++的內部機制有了更深的了解,不過也提示自己不能有僥倖心理,一定要養成良好的程式設計習慣,不管對於自己還是對於以後合作的夥伴都是一件好事。
C 類指標初始化
上面 的 會列印 a c 類指標定義的時候沒有初始化的時候,居然可以安全的呼叫類內部的成員函式而不出錯。在網上查了一下 初始化為null的類指標可以安全的呼叫不涉及類成員變數的類成員函式而不出錯,但是如果類成員函式中呼叫了類成員變數則會出錯,既然賦值為null的情況都可以使用,那麼自然不初始化的類指...
C 類指標初始化
上面的 會列印 a c 類指標定義的時候沒有初始化的時候,居然可以安全的呼叫類內部的成員函式而不出錯。在網上查了一下 初始化為null的類指標可以安全的呼叫不涉及類成員變數的類成員函式而不出錯,但是如果類成員函式中呼叫了類成員變數則會出錯,既然賦值為null的情況都可以使用,那麼自然不初始化的類指標...
類初始化問題
public class main extends a public void hello public static void main string args class a public void hello 結果 s null com.briup.main 15db9742 s hello ...