1. 程式人生 > >C++類物件空指標訪問成員函式

C++類物件空指標訪問成員函式

題目:

class A{
    public:
    void test(){printf("test A");}
};
int main(){
    A*pA=NULL;
    pA->test();
}

結果是輸出“test A”而不是程式崩潰,原因如下:

一種解釋:

A*pA=null;
pA->test();//當呼叫成員函式時,只是將實參null傳給this指標

  • test成員函式中並無任何需要通過this指標訪問的資料成員,因此沒有帶來任何影響

會崩潰的情況,呼叫的成員函式需要通過this指標訪問類的資料成語

#include<stdio.h>
class A{ public: void test1(){ printf("test 1"); } void test2(){ printf("test 2%d",data); } private: int data = 10; }; int main(){ A*pA = NULL; pA->test1();//成功輸出”test 1" pA->test2();//程式崩潰(注意不是編譯錯誤) return 0; }

結果:執行時崩潰
- 0x00E013E8 處有未經處理的異常(在 code_test.exe 中): 0xC0000005: 讀取位置 0x00000000 時發生訪問衝突。

更詳細的解釋:從c++靜態繫結談起

因為對於非虛成員函式,C++這門語言是靜態繫結的。這也是C++語言和其它語言Java, Python的一個顯著區別。以此下面的語句為例:

pA->test();
這語句的意圖是:呼叫物件 pA 的 test 成員函式。如果這句話在Java或Python等動態繫結的語言之中,編譯器生成的程式碼大概是:

找到 pA 的 test 成員函式,呼叫它。(注意,這裡的找到是程式執行的時候才找的,這也是所謂動態繫結的含義:執行時才繫結這個函式名與其對應的實際程式碼。有些地方也稱這種機制為遲繫結,晚繫結。)

但是對於C++。為了保證程式的執行時效率,C++的設計者認為凡是編譯時能確定的事情,就不要拖到執行時再查找了。所以C++的編譯器看到這句話會這麼幹:
1. 查詢 pA 的型別,發現它有一個非虛的成員函式叫 test 。(編譯器乾的)
2. 找到了,在這裡生成一個函式呼叫,直接調A:: test ( pA )。

所以到了執行時,由於 test ()函式裡面並沒有任何需要解引用 pA 指標的程式碼,所以真實情況下也不會引發segment fault。這裡對成員函式的解析,和查詢其對應的程式碼的工作都是在編譯階段完成而非執行時完成的,這就是所謂的靜態繫結,也叫早繫結。

正確理解C++的靜態繫結可以理解一些特殊情況下C++的行為。