C 中幾個值得分析的小問題(2)

2022-04-12 05:22:38 字數 3673 閱讀 9571

下面有3個小問題,作為c++ beginner你一定要知道錯在**了。

1、派生類到基類的引用或指標轉換一定「完美」存在?

一般情況,你很可能會認為:派生類物件的引用或指標轉換為基類物件的引用或指標是一件很正常的事。那要是不一般情況呢?請看下面這個例子:

class

person

string id; //

作為一般的人身份是「普通人」,作為學生身份是「學生」

};class student : private

person

};

仍然是以普通人和學生作為例子,學生是private派生自普通人的。id表示身份狀態(我將它宣告為public的,完全是為了方便,我仍然強烈建議宣告為private)。

void printid(const person& p)            //

列印id

我有下面這樣的呼叫:

person p;

student s;

printid(p);

//好的,沒有問題

printid(s); //

編譯出錯

好的,我已經注發布來了。編譯出錯,出錯資訊是:error c2243: 「型別轉換」: 從「student *」到「const person &」的轉換存在,但無法訪問。是不是有點跟你想的不大一樣呢?

分析:

很多時候,我們都預設public繼承,public繼承勾勒出一種is-a的關系,就是派生類是一種基類,從這個語義上理解似乎從派生類轉換為基類的自動的(引用或指標方式)。我這裡說的是private繼承,private繼承勾勒出一種「根據某物實現出」的關係。假設d以private派生於b,意思是d可以根據b物件實現得到。編譯器不會自動將派生類物件轉換為基類物件。

另外一點疑問,你可能注意到了printid如果傳入的是student物件,似乎訪問了私有成員id(private繼承),是不是這個原因導致編譯錯誤呢?我們將其改為下面這樣:

void printid(const person& p)            //

列印id

同樣編譯錯誤。所以,要注意:派生類到基類的引用或指標轉換一定「完美」存在,前提是public繼承,不能是private或protected繼承

說明:還要補充說明一下,在類的繼承層次中public為主,private有時候在設計上可以提供一點「便利」,protected我暫時還不知道有什麼好的用處。

2、關鍵字typename導致的編譯時錯誤。

這個問題,可能很多人都知道,也許像我一樣知道的「不全」。首先需要明確一點是:關鍵字typename與class在宣告template型別引數時完全一樣。我其實要說的是typename泛型(模板)中的使用,它可是大有所為的。

template內出現的名稱如果依賴於某個template引數,稱之為從屬名稱。如果從屬名稱在class內呈巢狀狀,稱之為巢狀從屬名稱。

template void printelement(const t&vec)

我們可能認為宣告了乙個指標,指向乙個t::const_iterator。實際上,t::const_iterator也可能是個名字為const_iterator的static變數,上面那行**就定義了乙個乘法。所以,巢狀從屬名稱可能導致解析困難

c++解析器有個簡單的規則:在template中遇到乙個巢狀從屬名稱,它會假設這個名稱不是個型別,這是預設情況,除非你用關鍵字typename主動告訴編譯器。

template void printelement(const t&vec)

這塊知識,基本每個人都知道。我其實想說的是,使用typename時,有幾個小點注意一下,因為typename的位置不僅在函式內。比如:

template void print(typename t::const_iterator iter)        //

形參表一定要使用typename

下面再舉個複雜一點的例子:

template 

class

base

private

:

intx;

};};template

class derived : public base::nested //

繼承列表,你不能使用typename

//建構函式初始化列表,你不能使用typename

};

注意,**中注釋的部分,就是typename兩個不能使用的地方。我再擴充套件一下這個問題:

template

void func(const typename base::nested& test) //

使用typename,很好的

我想問的是:derivedd(5);怎麼呼叫那個函式呢?正確是策略是:

func (d);                //

正確的func(d); //

錯誤的

3、建構函式、析構函式能否為虛函式?建構函式、析構函式能否呼叫虛函式?

這個小問題,真是源遠流長了。標準答案是不是都有了呢?我再不耐其煩的說一下。(我沒有把各家說法收集全,就是說下自己的理解)

(1)建構函式、析構函式能否為虛函式?

建構函式不能為虛函式。

回答:有好幾個點可以說說,我說的不全。

1、虛函式的作用主要是為了執行期根據base指標定址到「正確」的函式。既然是執行期,必然已經跨過編譯期了,那勢必物件都建立起來了,建構函式是初始化用的,那要建構函式何用。

2、如果我沒記錯的話,建構函式有乙個功能是建立vptr,注意不是vtbl(編譯期搞定的),假設是虛函式,誰來建立vptr,此時建構函式應該在vtbl裡待著呢。

3、虛函式是為了派生類對其進行改造的,但建構函式不能被繼承,談何改造。

等等…析構函式能不能為虛函式?

如果像stl那樣堅決不做派生類,我勸你別把它當虛函式,vptr好歹也是4個位元組的儲存呢。如果你要它作為派生類,我勸你一定要設定為虛函式,否則記憶體洩露會找上你。

(2)建構函式、析構函式能否呼叫虛函式?

其實這個問題,我一直都沒明白有啥意義。建構函式、析構函式呼叫虛函式是可以的,可是呼叫的是所屬類的版本

目錄:

c++中幾個值得分析的小問題(1)

c++中幾個值得分析的小問題(2)

C 中幾個值得分析的小問題(1)

下面3個小問題都是我認為c beginner應該能夠解答或辨別清楚的。希望我們能通過題目挖掘更多的資訊,而不僅僅侷限在解題。我最喜歡說的話 能力有限,所以作為拋磚引玉,希望共同討論,指出錯誤。另外,我都是碰到乙個覺得有必要記錄的問題,就寫下來說說,所以每一篇內容可能不是單一主題。1 先來看一道簡單題...

面試中的幾個小問題

面試中的幾個小問題 1 對stl中list封裝 參考1 2 對重要c函式實現 參考2 memset void memset void buffer,int c,int count memcpy void memcpy void dst,const void src,int count return ...

幾個常見的C 小問題

1.c 引用dll後,未能找到型別或命名空間名稱的問題 解決辦法 注意引用dll的程式集版本與當前程式集是否一致,即目標框架.net framwork版本是否一致。專案屬性 應用程式 解決辦法 把專案中 properties 目錄下的 license.licx 檔案刪除,再編譯就成功了。4.c 程式...