1. 程式人生 > >C++——對靜態綁定的理解

C++——對靜態綁定的理解

語句 類型 return font ron ati 成員變量 分析 意圖

看一個靜態綁定的例子:

 1 #include <iostream>
 2 
 3 using namespace std;
 4 
 5 class A
 6 {
 7 public:
 8     int a = 3;
 9     void test1()
10     {
11         printf("test A\n");
12     }
13     void test2()
14     {
15         cout << a;
16     }
17 };
18 
19 int main()
20 {
21     A *pA = NULL;
22     cout << pA.a;      //錯誤:NULL指針不能訪問類非static的成員變量
23     pA -> test1();     //正確:輸出test A
24     pA -> test2();     //錯誤:NULL指針不能訪問類非static的成員變量
25     return 0;
26 }

分析: 由於test是非虛函數,對於非虛成員函數,C++是靜態綁定的,即在編譯時就確定了,即編譯器在編譯的時候就知道。空對象指針不能訪問非static成員變量,但可以訪問成員函數( 非虛函數 ),因為數據成員要分配內存才能訪問,而函數是不需要的。

解析(1):

pA->test1 ;

這語句的意圖是:調用對象 pA 的 test 成員函數。如果這句話在Java或Python等動態綁定的語言之中,編譯器生成的代碼大概是:

找到 pA 的 test 成員函數,調用它。(註意,這裏的找到是程序運行的時候才找的,這也是所謂動態綁定的含義:運行時才綁定這個函數名與其對應的實際代碼。有些地方也稱這種機制為遲綁定,晚綁定。)


但是對於C++。為了保證程序的運行時效率,C++的設計者認為凡是編譯時能確定的事情,就不要拖到運行時再查找了。所以C++的編譯器看到這句話會這麽幹:
1:查找 pA 的類型,發現它有一個非虛的成員函數叫 test 。(編譯器幹的)
2:找到了,在這裏生成一個函數調用,直接調A:: test ( pA )。
所以到了運行時,由於 test ()函數裏面並沒有任何需要解引用 pA 指針的代碼,所以真實情況下也不會引發segment fault。這裏對成員函數的解析,和查找其對應的代碼的工作都是在編譯階段完成而非運行時完成的,這就是所謂的靜態綁定,也叫早綁定。
正確理解C++的靜態綁定可以理解一些特殊情況下C++的行為。

解析(2):

test函數作為非虛函數,在編譯時就確定了。即使pA為null,但是已經聲明了類型,就知道pA有個test函數,且test函數裏沒有用到成員變量,單單一個打印語句是可以運行成功的。

本題中NULL指針調用成員函數的時候成員函數中沒有成員變量,如果訪問此對象的成員函數,則程序崩潰,因為this指針是NULL

C++——對靜態綁定的理解