c++ 編譯期與執行期總結
一 見識編譯期的力量
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 |
#include
"stdafx.h"
#include
<iostream>
using namespace std;
class A{
public :
virtual void f(){
cout << "A" <<
endl; }
};
class B
: public A
{
private :
void f(){
cout << "B" <<
endl; }
};
int _tmain( int argc,
_TCHAR* argv[])
{
A
*pA = new B();
pA->f();
return 0;
}
|
出現了很有趣的結果,在main函式中,訪問到了B類中的私有方法。為什麼會出現這樣的結果呢?
一般來說外部物件訪問類的私有成員,除非是友元,否則在編譯的時候就會報錯,但是上面那段程式碼卻可以正常的編譯通過。
C++因為支援面向物件程式設計,制訂了一系列實現策略的語言機制。其中,各種各樣的“限制”主要是出現在編譯時:因此如果直接B d; d.f(); 就會導致編譯錯誤:編譯器發現
B::f()是B類的私有成員函式,因此拒絕這樣的訪問。
這裡我們可以區分類和物件:類是編譯期的概念,也是“訪問許可權”、“成員資料”、“成員函式”這幾個概念的“作用域”。而物件的作用域是執行期。它包括類的例項、引用和指標。
參考:編譯執行
A *pA = new B(); 這裡 pA 是一個 A* , 所以就作為一個A類的指標參與了編譯;
因此從pA呼叫f()在編譯器眼中,就是呼叫了A類的公開成員函式f()因此通過編譯;然後在執行時,由於多型作用pA呼叫的f()是派生類的f()成員函式。
雖然這時f()是private成員函式,但是由於 private/public 這些訪問控制是編譯時的限制,在執行時無效,所以B::f() 被成功呼叫。
如果能夠理解這兩個在不同時間作用的概念,這個問題就很好理解.
2 上面的例子是轉載的,裡面有個例子是輸出除錯資訊的,
1)
const bool g_debug=1
void Log(char* str) {
if (g_debug){
printf(str);
}
}
2) #define DEBUG
#ifdef DEBUG
#define LOG(m) \
{\
printf(m);
\ }\
#else
#define LOG(m)
#endif
有經驗的程式設計師肯定一眼就看出來第二種Log輸出方式比較好。第一種的判斷是在執行期,而第二種的判斷是在編譯期。第一種在每次執行log函式的時候都會去判斷全域性變數,
而第二種是在編譯的時候就已經將不需要的Log資訊定義為空。因為編譯消耗的時間是一次性的,而執行的效率是直接呈現給使用者的。由這種節約時間的方法,
我們可以發現把很多函式的判斷和計算放在編譯期,會大大節約執行的時間,提供執行效率,但是編譯器並不是萬能,所以編譯期能做的工作還是十分的有限。
二 編譯期 執行期 說明
1. 編譯期是指把你的源程式交給編譯器編譯的過程,最終目的是得到obj檔案,連結後生成可執行檔案(預處理、編譯、彙編和連線)。執行期指的是你將可執行檔案交給作業系統(輸入檔名,回車)執行、直到程式執行結束。執行的目的是為了實現程式的功能 。
2.編譯期分配記憶體,就是用靜態或全域性陣列。這是在編譯的時候確定的。
編譯期記憶體錯誤,就是比如某個資料段DATA段或者CODE段等等,超過跑這個程式的目標機的儲存器的限制。比如DOS下,DATA段不能超過64K吧。
執行期分配記憶體,就是用malloc()之類的函式,在堆上分配記憶體。 執行期記憶體錯誤,就是執行的時候發生的,比如申請不到記憶體,記憶體越界訪問,等等。
除了記憶體外,編譯器還做了巨集替換,檢查型別安全,上面一說的,類的“訪問許可權”、“成員資料”、“成員函式”這幾個概念的“作用域”等等的語法檢查,其他的我就不知道了。
三 編譯期執行的效率
編譯期,把每一步操作結果的值都給快取下來了,效率當然高了,我們使用相同的快取策略也可以縮短函式的執行時間。
執行期執行的函式則沒有這個優點,它和普通的函式沒有區別,不同點在於它在編譯的時候執行。把編譯器看成一種動態語言執行環境,那麼編譯“編譯期執行的函式”的過程就像是執行動態語言程式的過程。
如果程式中存在大量編譯期執行函式,可能極大地降低編譯效率,對於它的使用應該儘量避免。
模板提供的是一種宣告式語法,在複雜的靜態執行邏輯面前有時候會顯得力不從心,編譯期執行函式在解決這個問題的同時,卻帶來了上面這種副作用。
如果能把兩者的優點結合起來用函式的語法來寫程式,由編譯器生成優化的模板程式碼,這種編譯期執行才可以大量使用。
同樣,可以寫個梯乘的函式,求10000,在編譯器和執行期,各寫一個例子看看執行結果。就知道編譯期做運算的好壞。
四 模板的運用 1 stl 模板是在編譯期執行的,所以程式中儘量用stl,除了安全性與規範性好外,stl的一些操作都在編譯時執行。
2 ATL ATL 跟com有個區別就是用了模板來取代com的虛擬表。ATL用了大量模板,很多操作都在編譯時執行了, 所以編出來的可執行檔案,比較小。這個是我自己猜的。
3 泛型程式語言,C++強大(但容易失控的)模板功能使它能在編譯期完成許多工作,從而大大提高執行期效率。這個東西比較難,暫時沒詳細瞭解。一個不錯 的例子: