# [33] 成員函數指針
## FAQs in section [33]:
* [33.1] “成員函數指針”類型不同于“函數指針”嗎?
* [33.2] 如何將一個成員函數指針傳遞到信號處理函數,X事件回調函數,系統調用來啟動一個線程/任務等?
* [33.3] 為什么我總是收到編譯錯誤(類型不匹配)當我嘗試用一個成員函數作為中斷服務例程? when I try to use a member function as an interrupt service routine?")
* [33.4] 為什么取C++函數的地址我會遇到問題?
* [33.5] 使用成員函數指針調用函數時我如何才能避免語法錯誤?
* [33.6] 如何創建和使用一個成員函數指針數組?
* [33.7] 可以轉換成員函數指針為void *嗎?
* [33.8] 可以轉換函數指針為void *嗎?
* [33.9] 我需要類似函數指針的功能,但需要更多的靈活性和/或線程安全,是否有其他方法?
* [33.10] 什么是functionoid,為什么我要使用它?
* [33.11] 可以讓functionoids快于正常的函數調用嗎?
* [33.12] functionoid和仿函數(functor)有什么區別? ??
## 33.1 “成員函數指針”類型不同于“函數指針”嗎?
對。
考慮下面的函數:
`?int?f(char?a,?float?b);`
函數的類型不同取決于它是否是一個普通函數或某些類非靜態成員函數:
* 它的類型是“int(*)(char, float)”,如果它是一個普通的函數
* 它的類型是“`int (Fred::*)(char,float)`”,如果它是類Fred的非靜態成員函數
注意:如果它是類Fred的靜態成員函數 ,它的類型和普通函數是相同的:“int(*)(char, float)”。
## 33.2 如何將一個成員函數指針傳遞到信號處理函數,X事件回調函數,系統調用來啟動一個線程/任務等?
不要。
由于成員函數是沒有意義,如果沒有一個對象來觸發的話,所以你不能這樣直接調用(如X窗口系統代碼用C++重寫的話,它很可能會傳遞對象的引用,不僅僅是函數指針,當然了對象將包含所需的函數,甚至更多)。
作為對現有軟件的補丁,使用頂級(top-level)函數(非成員函數)作為包裝器,包裝器接受通過一些其他技術實例化的對象為參數。取決于你要調用的函數,這個“其他技術”有可能很瑣碎或者不需要你做太多的工作。對于啟動一個新線程的系統調用,例如,可能要求你傳遞一個 void *類型的函數指針,這種情況下你可以傳遞void*類型的對象指針。許多實時操作系統要啟動一個新任務時候于此類似。最壞的情況,你可以將對象指針存儲在全局變量中,對于Unix的信號處理程序來說可能需要這樣處理(但一般來說不希望使用全局變量)。在任何情況下,頂級(top-level)函數將負責調用相應的對象的成員函數。
下面是一個最壞的例子(要使用全局變量)。中斷發生時候假設你要調用 `Fred::memberFn()` :
```
?class?Fred?{
?public:
???void?memberFn();
???static?void?staticMemberFn();??//?A?static?member?function?can?usually?handle?it
...
?};
//?Wrapper?function?uses?a?global?to?remember?the?object:
?Fred*?object_which_will_handle_signal;
?void?Fred_memberFn_wrapper()
?{
???object_which_will_handle_signal->memberFn();
?}
?int?main()
?{
/*?signal(SIGINT,?Fred::memberFn);&?*/ //?Can?NOT?do?this
???signal(SIGINT,?Fred_memberFn_wrapper);??//?OK
???signal(SIGINT,?Fred::staticMemberFn);???//?OK?usually;?see?below
...
?}
```
注: 靜態成員函數并不需要一個實際對象來觸發,因此靜態成員函數指針和普通函數指針“通常”是兼容的。然而,盡管它可能在大多數編譯器上工作,但是嚴格來說它必須是帶有`extern"C"`修飾的非成員函數。因為“C鏈接器”不僅不知道“名字校正(mangle)”等,而且還不知道不同的調用約定,而C和C++的調用約定可能不同。
## 33.3 為什么我總是收到編譯錯誤(類型不匹配)當我嘗試用一個成員函數作為中斷服務例程?
這是前兩個問題的特殊情況,因此,閱讀前兩個FAQ問題的答案。
非靜態成員函數有一個隱藏的參數,對應于`this`指針,該`this`指針指向的對象的實例。系統的中斷硬件/固件不能提供有關`this`指針參數。你必須使用“普通”函數(非類成員)或靜態成員函數作為中斷服務例程。
一個可行的辦法是使用一個靜態成員函數作為中斷服務程序,并讓該靜態函數去負責查找在中斷時候應該調用的實例/成員函數。實際效果是,中斷的時候成員函數被調用,但是出于技術原因你需要調用一個中間函數。
## 33.4 為什么取C++函數的地址我會遇到問題?
簡單答案:如果你試圖把它存儲到(或者傳遞到)函數指針,這就會產生問題-這是前面FAQ問題的必然結果。
詳細回答:在C++成員函數有一個隱含的參數,它指向對象(內部成員函數的this指針)。普通C函數和成員函數有不同的函數調用約定,所以他們的指針類型(成員函數指針與普通函數指針)是不同的,不相容的。C++中引入了新的指針類型,稱為成員指針,它只能供一個實例對象調用。
注意: 不要試圖強制轉換成員函數指針為普通函數指針,結果是不確定的,可能是災難性的。例如,一個成員函數指針不需要包含確切函數的機器地址。正如在最后一個例子,如果你有一個普通C函數的指針,使用一個頂層(非成員)函數或靜態 (類)成員函數。
## 33.5 使用成員函數指針調用函數時我如何才能避免語法錯誤?
同時使用 `typedef` _和_ `#define`宏。
**步驟1:**創建typedef:
```
?class?Fred?{
?public:
???int?f(char?x,?float?y);
???int?g(char?x,?float?y);
???int?h(char?x,?float?y);
???int?i(char?x,?float?y);
...
?};
//?FredMemFn?points?to?a?member?of?Fred?that?takes?(char,float)
?typedef??int?(Fred::*FredMemFn)(char?x,?float?y);
```
**第2步:**創建一個`#define`宏:
```
#define?CALL_MEMBER_FN(object,ptrToMember)??((object).*(ptrToMember))
```
( 通常我不喜歡`#define`宏,但在成員函數指針中你應該使用他們,因為他們可以提高可讀性和代碼的易用性。)
以下是如何使用這些功能:
```
?void?userCode(Fred&?fred,?FredMemFn?memFn)
?{
???int?ans?=?CALL_MEMBER_FN(fred,memFn)('x',?3.14);
//?Would?normally?be:?int?ans?=?(fred.*memFn)('x',?3.14);
...
?}
```
我強烈建議使用這些功能。在實踐中,成員函數調用更比剛才復雜,可讀性和代碼的易寫性的區別很大。[comp.lang.C++](news:comp.lang.c++)不得不忍受成千上萬的程序員的詢問語法錯誤的帖子,。幾乎所有這些錯誤都會消失如果他們使用了這些功能。
注:#define宏有4中罪惡: 罪惡#1 ,? 罪惡#2 ,? 罪惡#3 和罪惡#4 。但有時他們仍然有用。只要別忘了使用后洗清“罪惡”的雙手。
## 33.6 如何創建和使用一個成員函數指針數組?
_同時_使用`typedef` _和_ `#define`宏的前面描述,你就完成90%。
**步驟1:**創建typedef:
```
?class?Fred?{
?public:
???int?f(char?x,?float?y);
???int?g(char?x,?float?y);
???int?h(char?x,?float?y);
???int?i(char?x,?float?y);
...
?};
//?FredMemFn?points?to?a?member?of?Fred?that?takes?(char,float)&
?typedef??int?(Fred::*FredMemFn)(char?x,?float?y);
```
**第2步:**創建一個`#define`宏:
```
#define?CALL_MEMBER_FN(object,ptrToMember)??((object).*(ptrToMember))
```
現在簡單地創建成員函數的指針數組:
```
FredMemFn?a[]?=?{?&Fred::f,?&Fred::g,?&Fred::h,?&Fred::i?};
```
也可以簡單地調用成員函數的指針:
```
?void?userCode(Fred&?fred,?int?memFnNum)
?{
//?Assume? memFnNum ?is?between?0?and?3?inclusive:
???CALL_MEMBER_FN(fred,?a[memFnNum])?('x',?3.14);
?}
```
注:#define宏有4中罪惡: 罪惡#1 ,? 罪惡#2 ,? 罪惡#3 和罪惡#4 。但有時他們仍然有用。雖然感到恥辱和負罪感,如果像宏這樣的結構如果能夠改進你的軟件,那么就使用它。
## 33.7 可以轉換成員函數指針為`void *`嗎?
否!
```
?class?Fred?{
?public:
???int?f(char?x,?float?y);
???int?g(char?x,?float?y);
???int?h(char?x,?float?y);
???int?i(char?x,?float?y);
...
?};
//?FredMemFn?points?to?a?member?of?__Fred__?that?takes?(char,float)
?typedef??int?(Fred::*FredMemFn)(char?x,?float?y);
?#define?CALL_MEMBER_FN(object,ptrToMember)??((object).*(ptrToMember))
?int?callit(Fred&?o,?FredMemFn?p,?char?x,?float?y)
?{
???return?CALL_MEMBER_FN(o,p)(x,?y);
?}
?int?main()
?{
???FredMemFn?p?=?&Fred::f;
???void*?p2?=?(void*)p;??????????????????//?←?illegal!!
???Fred?o;
???callit(o,?p,?'x',?3.14f);?????????????//?okay
???callit(o,?FredMemFn(p2),?'x',?3.14f);?//?might?fail!!
...
?}
```
_請_不要給我發電子郵件, 如果碰巧上述情況_在_您的特定的操作系統和特定的編譯器的特定版本中沒有問題。我不在乎這些。這中做法是非法的,句號!
## 33.8 可以轉換函數指針為`void *`嗎?
否!
```
?int?f(char?x,?float?y);
?int?g(char?x,?float?y);
?typedef?int(*FunctPtr)(char,float);
?int?callit(FunctPtr?p,?char?x,?float?y)
?{
???return?p(x,?y);
?}
?int?main()
?{
???FunctPtr?p?=?f;
???void*?p2?=?(void*)p;??????????????//?←?illegal!!
???callit(p,?'x',?3.14f);????????????//?okay
???callit(FunctPtr(p2),?'x',?3.14f);?//?might?fail!!
...
?}
```
_請_不要給我發電子郵件, 如果碰巧上述情況_在_您的特定的操作系統和特定的編譯器的特定版本中沒有問題。我不在乎這些。這中做法是非法的,句號!
## 33.9 我需要類似函數指針的功能,但需要更多的靈活性和/或線程安全,是否有其他方法?
使用functionoid。
## 33.10 什么是functionoid,為什么我要使用它?
Functionoids是基于steroids的函數。嚴格來說比函數功能更強大,而其額外的功能解決了使用函數指針時所面臨的一些(不是全部)的挑戰。
讓我們舉一個例子說明傳統函數指針的使用,然后我們將其轉化為使用functionoids的例子。傳統的函數指針的思想是定義一堆兼容的函數:The traditional function-pointer idea is to have a bunch of compatible functions:
```
?int?funct1(...params...)?{?...code... }
?int?funct2(...params...)?{?...code...?}
?int?funct3(...params...)?{?...code...?}
```
然后,你通過函數指針來調用:
```
?typedef?int(*FunctPtr)(...params...);
?void?myCode(FunctPtr?f)
?{
...
???f(...args-go-here...);
...
?}
```
有時,人們創建函數指針數組:
```
?FunctPtr?array[10];
?array[0]?=?funct1;
?array[1]?=?funct1;
?array[2]?=?funct3;
?array[3]?=?funct2;
...
```
在這種情況下,通過訪問該數組來調用函數:
```
?array[i](...args-go-here...);
```
使用functionoids,首先創建了有一個純虛函數的的基類:
```
?class?Funct?{
?public:
???virtual?int?doit(int?x)?=?0;
???virtual?~Funct()?=?0;
?};
?inline?Funct::~Funct()?{?}??//?defined?even?though?it's?pure?virtual;?it's?faster?this?way;?trust?me
```
然后,你可以創建三個派生類來替代3個函數:
```
?class?Funct1?:?public?Funct?{
?public:
???virtual?int?doit(int?x)?{?...code?from?funct1...?}
?};
?class?Funct2?:?public?Funct?{
?public:
???virtual?int?doit(int?x)?{?...code?from?funct2...?}
?};
?class?Funct3?:?public?Funct?{
?public:
???virtual?int?doit(int?x)?{?...code?from?funct3...?}
?};
```
然后,不是傳遞一個函數指針而是傳遞一個`Funct *`。我創建 `typedef`稱為`FunctPtr`,只是為了代碼看起來類似以前的方法:
```
?typedef?Funct*?FunctPtr;
?void?myCode(FunctPtr?f)
?{
...
???f->doit(...args-go-here...);
...
?}
```
你可以用同樣的方式來創建數組:
```
?FunctPtr?array[10];
?array[0]?=?new?Funct1(_...ctor-args..._);
?array[1]?=?new?Funct1(_...ctor-args..._);
?array[2]?=?new?Funct3(_...ctor-args..._);
?array[3]?=?new?Funct2(_...ctor-args..._);
...
```
首先這給出了一個functionoids比函數指針功能更強大的事實,即functionoid可以傳遞參數可以傳遞到構造函數(如上圖所示的ctor - argS)而函數指針版本則沒有。可以想象functionoid對象為一個freeze-dried函數調用(重點在調用這個詞)。 不像一個函數指針,functionoid是(概念上)一個指向了部分被調用函數的指針。想象目前的技術,讓你通過傳遞一部分,但是不是全部參數給一個函數,然后讓你freeze-dry(部分完成)函數調用。就好像這種技術可讓你使用某種神奇的指針,指針指向那個freeze-dry部分完成的函數調用。然后你通過使該指針傳遞其余參數,系統神奇地結合你原來傳遞的參數(即是freeze-dried的參數),結合函數先前計算的局部變量(被freeze-dried之前),加上所有新傳遞的`args`,從函數上次被freeze-dried的地方開始繼續執行函數 。這聽起來像是科幻小說,但它正是概念上functionoids可以辦到的。 另外 ,它可以讓你反復地使用各種不同的“剩余的參數”來“完成”freeze-dried函數調用,你要你喜歡,多少次都可以。另外 ,允許(不是必須)你改變freeze-dried的狀態當調用的時候,這意味著functionoids可以記得從一個調用到下一個的信息。
好吧,讓我們回到現實,我會舉一兩個例子來解釋上面敘述的意義。
假設原有函數(在老式的函數指針樣式下)采取略有不同的參數。
```
?int?funct1(int?x,?float?y)
?{?...code...?}
?int?funct2(int?x,?const?std::string&?y,?int?z)
?{?...code...?}
?int?funct3(int?x,?const?std::vector<double>&?y)
?{?...code...?}
```
當參數不同的時候,老式的函數指針的方法是很難湊效,因為函數調用方不知道需要傳遞哪些參數(呼叫者僅僅有一個函數指針,而不是函數的名稱或,當參數不同的時候需要的參數個數和參數類型)(不要給我發送電子郵件,我承認你可以做到這一點,但你必須花費很多精力并且收拾殘局。無論如何不要給我寫郵件 –請使用functionoids代替)。
使用functionoids有時情況會好很多。由于functionoid可以看作是一個freee-dried函數調用 ,只需象上面的`y`和/或者`z`一樣,可以傳遞它們到相應的構造函數。你還可以通過共同`args`參數(在上例中的 `int `類型的`x`參數)到`ctor`,但你不必-這樣做。你也可以直接傳遞他們到的純虛函數`doIt()`。 下面假設你想傳遞X 到`doIt()`和傳遞`y`和/或 `z`到構造函數:
```
?class?Funct?{
?public:
???virtual?int?doit(int?x)?=?0;
?};
```
然后,你可以創建三個派生類,而不是三個函數:
```
?class?Funct1?:?public?Funct?{
?public:
???Funct1(float?y)?:?y_(y)?{?}
???virtual?int?doit(int?x)?{?...code?from?funct1...?}
?private:
???float?y_;
?};
?class?Funct2?:?public?Funct?{
?public:
???Funct2(const?std::string&?y,?int?z)?:?y_(y),?z_(z)?{?}
???virtual?int?doit(int?x)?{?_...code?from?funct2..._?}
?private:
???std::string?y_;
???int?z_;
?};
?class?Funct3?:?public?Funct?{
?public:
???Funct3(const?std::vector<double>&?y)?:?y_(y)?{?}
???virtual?int?doit(int?x)?{?_...code?from?funct3..._?}
?private:
???std::vector<double>?y_;
?};
```
當你創建的functionoids數組的時候,構造函數的參數被freeze-dried到functionoid:
```
?FunctPtr?array[10];
?array[0]?=?new?Funct1(3.14f);
?array[1]?=?new?Funct1(2.18f);
?std::vector<double>?bottlesOfBeerOnTheWall;
?bottlesOfBeerOnTheWall.push_back(100);
?bottlesOfBeerOnTheWall.push_back(99);
...
?bottlesOfBeerOnTheWall.push_back(1);
?array[2]?=?new?Funct3(bottlesOfBeerOnTheWall);
?array[3]?=?new?Funct2("my?string",?42);
...
```
因此,當用戶在調用這些functionoids的`doIt()`的時候,他提供的“剩余”`args`,函數調用會把傳遞到構造函數與傳遞到`doIt()`的參數結合起來:
```
array[i]->doit(12);
```
正如我以前說的,functionoids的優點之一是,你可以有多個實例,比方說在你的數組里面Funct1,這些實例可以有不同的參數,被freeze-dried到構造函數。例如, 數組`[0]`和數組`[1]`的類型都是`Funct1`,但數組`[0] -> doIt(12)`的行為和數組`[1] –>doIt(12)`的行為是不一樣的,因為這將取決于傳遞給調用`doIt(`)函數的12 和傳遞給構造函數的 `args`。
如果我們把functionoids數組的例子變為一個本地的functionoid,你將會看到functionoids的另一個優點。為了熱身,讓我們回到老式的函數指針的方法,想象你要傳遞一個比較函數到`sort()`或`binarySearch()`例程。`sort()`或`binarySearch()`例程被稱作`childRoutine()`和比較函數指針類型被稱為`FunctPtr`:
```
?void?childRoutine(FunctPtr?f)
?{
...
???f(...args...);
...
?}
```
然后,不同的調用方根據自己的判斷傳遞不同的函數指針:
```
?void?myCaller()
?{
...
???childRoutine(funct1);
...
?}
?void?yourCaller()
?{
...
???childRoutine(funct3);
...
?}
```
我們可以很容易地轉化為一個使用functionoids的例子:
```
?void?childRoutine(Funct&?f)
?{
...
???f.doit(_...args..._);
...
?}
?void?myCaller()
?{
...
???Funct1?funct(_...ctor-args..._);
???childRoutine(funct);
...
?}
?void?yourCaller()
?{
...
???Funct3?funct(_...ctor-args..._);
???childRoutine(funct);
...
?}
```
鑒于這樣的例子,我們可以看到functionoids優于函數指針的兩個好處。上面講述了在“ctor args”的好處,再加上functionoids能夠在一個線程安全的環境下保持調用之間的狀態。與普通的函數指針相比,人們通常通過使用靜態數據來保持狀態,不過靜態數據是在本質上不是線程安全的---所有線程共享靜態數據。但是functionoid方法本質上是線程安全的,因為這些代碼是與線程本地數據想關聯的。實現是很瑣碎的:改變老式的靜態數據為一個functionoid對象實例; 并且該實現可以證明數據不僅是線程局部的,而且也可以安全的進行遞歸調用:每次調用`yourCaller()`將有自己獨特的有自己獨特的數據成員的`Funct3`對象實例。
請注意,我們已經得到了一些東西,但是不用付出任何代價。如果你想線程全局的數據,functionoids可以實現:只需更改的實例數據成員為functionoid的靜態成員,或者局部范圍的靜態數據。該實現和函數指針相比是伯仲之間。
functionoid為你提供了第三種選擇,而老式的函數指針方法卻不行:允許functionoid的調用方決定他們是否希望線程局部或線程全局的數據。如果調用方希望線程全局的數據,他們需要負責的線程安全,至少他們可以有這個選擇。這很容易:
```
?void?callerWithThreadLocalData()
?{
...
???Funct1?funct(...ctor-args...);
???childRoutine(funct);
...
?}
?void?callerWithThreadGlobalData()
?{
...
???static?Funct1?funct(...ctor-args...);??←?the?static?is?the?only?difference
???childRoutine(funct);
...
?}
```
Functionoids不能解決遇到的每一個問題當需要編寫柔性軟件的時候,但嚴格來講他們比函數指針功能更強大,至少需要評估一下。事實上,你可以很容易證明functionoids擁有函數指針的所有功能,因為可以想像,老式函數指針相當于一個全局的(!)functionoid對象。既然你總是可以定義functionoid全局對象,你自然沒有失去任何東西。證畢!
## 33.11 可以讓functionoids快于正常的函數調用嗎?
是。
如果你有一個非常小的functionoid,并在實際應用中的相當常見,函數調用本身的成本可能會很高,與由functionoid完成工作的成本相比。在以前的FAQ中,functionoids的實現使用了虛函數,這通常會花費一個函數調用成本。另一種方法使用的模板 。
下面的例子與以前的FAQ類似。我把調用`doIt()`修改為運算符`()()`來改善代碼的可讀性,也允許別人傳遞普通函數指針:
```
?class?Funct1?{
?public:
???Funct1(float?y)?:?y_(y)?{?}
???int?operator()(int?x)?{?...code?from?funct1...?}
?private:
???float?y_;
?};
?class?Funct2?{
?public:
???Funct2(const?std::string&?y,?int?z)?:?y_(y),?z_(z)?{?}
???int?operator()(int?x)?{?...code?from?funct2...?}
?private:
???std::string?y_;
???int?z_;
?};
?class?Funct3?{
?public:
???Funct3(const?std::vector<double>&?y)?:?y_(y)?{?}
???int?operator()(int?x)?{?...code?from?funct3...?}
?private:
???std::vector<double>?y_;
?};
```
這種做法,在以前的FAQ的區別是fuctionoid在編譯時而不是在運行時被“綁定”。想象你把它作為一個參數傳遞:如果你在編譯時已經知道你最終要傳遞的functionoid,那么你可以使用以上技術,至少在典型的情況下](inline-functions.html#faq-9.3)你可以獲得一個相對速度優勢,就是編譯器[內聯代碼到調用方。下面是一個例子:
```
?template?<typename?FunctObj>
?void?myCode(FunctObj?f)
?{
...
???f(...args-go-here...);
...
?}
```
編譯器編譯上面代碼的時候,有可能內聯展開的函數調用,即可能提高性能。
下面是一種調用方法:
```
?void?blah()
?{
...
???Funct2?x("functionoids?are?powerful",?42);
???myCode(x);
...
?}
```
補充:正如在上文第一段所述,你也可以傳遞普通函數(盡管調用方調用時可能會招致一些花銷):
```
?void?myNormalFunction(int?x);
?void?blah()
?{
...
???myCode(myNormalFunction);
...
?}
```
## 33.12 functionoid和仿函數(functor)有什么區別? ?
functionoid是一個對象,有一個主要方法。它基本上是C函數的面向對象擴展,人們會使用functionoid當函數有多個入口點(即不止一個“method”),和/或者需要以線程安全的方式(C風格的解決辦法是,增加一個本地的“靜態”變量,但在多線程環境中不能保證線程安全)調用之間保持狀態。
functor是functionoid的特殊情況:這是一個其方法是“函數調用操作符”(`operator()()`)的functionoid. 由于它重載函數調用操作符,代碼可以使用和函數調用相同的語法來調用它的主體方法。例如,如果“`foo`”是一個functor,要調用“`foo`”對象的“`operator()()`”可以使用“`foo()`”。在這樣的好處在于模板,模板可以有一個可以作為函數使用的模板參數,這個參數可以是一個函數或仿函數對象。它有一個性能優勢,就是仿函數對象的 “`operator()()`"方法可以被內聯(如果你傳遞一個函數地址,那么它不能被內聯)。
這是非常有用的,比如對于排序容器“比較”函數。在C中,比較函數總是通過指針傳遞(例如,參見 “`qsort()`"聲明),但在C++中參數可以是函數指針或者functor對象,其導致的結果就是C++的排序容器在某些情況下,要比C語言中的更快(不慢)。
由于Java沒有任何類似模板的功能,它必須使用動態綁定,動態綁定必然意味著函數調用。這通常不是什么大問題,但在C++中,我們要讓代碼發揮最高性能,也就是說,C++中有一個“pay for it only if you use it”的理念,這意味著語言絕對不能隨意施加任何開銷到物理機器(當然是程序員有可能會,比如選擇的使用如動態綁定等技術,施加一些開銷,這是作為的靈活性或其他“特性”的交換,應該由設計師和程序員來決定他們是否想要這些結構帶來的好處(和成本等)。
- C++ FAQ Lite
- [1] 復制許可
- [2] 在線站點分發本文檔
- [3] C++-FAQ-Book 與 C++-FAQ-Lite
- [6] 綜述
- [7] 類和對象
- [8] 引用
- [9] 內聯函數
- [10] 構造函數
- [11] 析構函數
- [12] 賦值算符
- [13] 運算符重載
- [14] 友元
- [15] 通過 &lt;iostream&gt; 和 &lt;cstdio&gt;輸入/輸出
- [16] 自由存儲(Freestore)管理
- [17] 異常和錯誤處理
- [18] const正確性
- [19] 繼承 — 基礎
- [20] 繼承 — 虛函數
- [21] 繼承 — 適當的繼承和可置換性
- [22] 繼承 — 抽象基類(ABCs)
- [23] 繼承 — 你所不知道的
- [24] 繼承 — 私有繼承和保護繼承
- [27] 編碼規范
- [28] 學習OO/C++
- [31] 引用與值的語義
- [32] 如何混合C和C++編程
- [33] 成員函數指針
- [35] 模板 ?
- [36] 序列化與反序列化
- [37] 類庫