Demo
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++的行爲。