c++知識總結目錄索引
二、哪些函式不能宣告成虛函式
三、為什麼析構函式和建構函式內不能呼叫虛函式
四、為什麼析構函式最好宣告成虛函式
五、實現乙個不能被繼承的類
六、實現乙個類定義出來的物件都在堆上面
七、實現乙個類定義出來的物件都在棧上面
八、實現乙個類,定義出的物件不能在堆上
九、用c語言實現繼承和多型
c++
中,並不是所有的成員函式都能被子類繼承,有三類成員函式不能被子類繼承,分別是:建構函式(包括拷貝構造)、析構函式、賦值運算子過載函式。
構造方法用來初始化類的物件,與父類的其它成員不同,它不能被子類繼承(子類可以繼承父類所有的成員變數和成員方法,但不繼承父類的構造方法)。因此,在建立子類物件時,為了初始化從父類繼承來的資料成員,系統需要呼叫其父類的構造方法。
如果沒有顯式的建構函式,編譯器會給乙個預設的建構函式,並且該預設的建構函式僅僅在沒有顯式地宣告建構函式情況下建立。
構造原則如下:
1. 如果子類沒有定義構造方法,則呼叫父類的無引數的構造方法。
2. 如果子類定義了構造方法,不論是無引數還是帶引數,在建立子類的物件的時候,首先執行父類無引數的構造方法,然後執行自己的構造方法。
3. 在建立子類物件時候,如果子類的建構函式沒有顯示呼叫父類的建構函式,則會呼叫父類的預設無參建構函式。
4. 在建立子類物件時候,如果子類的建構函式沒有顯示呼叫父類的建構函式且父類自己提供了無參建構函式,則會呼叫父類自己的無參建構函式。
5. 在建立子類物件時候,如果子類的建構函式沒有顯示呼叫父類的建構函式且父類只定義了自己的有參建構函式,則會出錯(如果父類只有有引數的構造方法,則子類必須顯示呼叫此帶參構造方法)。
6. 如果子類呼叫父類帶引數的構造方法,需要用初始化父類成員物件的方式
析構函式也不會被子類繼承,只是在子類的析構函式中會呼叫父類的析構函式。
賦值運算子過載函式也不會被子類繼承,只是在子類的賦值運算子過載函式中會呼叫父類的賦值運算子過載函式。
在c++
,有五種函式不能被宣告成虛函式,分別是:非成員函式、建構函式、靜態成員函式、內聯成員函式、友元函式這五種,下面分別解釋為什麼這五種函式不能被宣告成虛函式。
非成員函式只能被過載(overload),不能被繼承(override),而虛函式主要的作用是在繼承中實現動態多型,非成員函式早在編譯期間就已經繫結函式了,無法實現動態多型,那宣告成虛函式還有什麼意義呢?
要想呼叫虛函式必須要通過「虛函式表」來進行的,但虛函式表是要在物件例項化之後才能夠進行呼叫。而在建構函式執行期間,還沒有為虛函式表分配空間,自然就沒法呼叫虛函式了。
靜態成員函式對於每個類來說只有乙份,所有的物件都共享這乙份**,它是屬於類的而不是屬於物件。虛函式必須根據物件型別才能知道呼叫哪乙個虛函式,故虛函式是一定要在物件的基礎上才可以的,兩者乙個是與例項相關,乙個是與類相關。
內聯函式是為了在**中直接展開,減少函式呼叫花費的代價,虛函式是為了在繼承後物件能夠準確的執行自己的動作,並且inline函式在編譯時被展開,虛函式在執行時才能動態地繫結函式。
因為c++不支援友元函式的繼承,對於沒有繼承特性的函式沒有虛函式的說法。友元函式不屬於類的成員函式,不能被繼承。
在構造派生類物件時,首先呼叫基類建構函式初始化物件的基類部分,再呼叫派生類建構函式。在執行基類建構函式時,物件的派生類部分是未初始化的。實際上,此時的物件還不是乙個派生類物件(不完整)。
析構派生類物件時,首先呼叫的是派生類析構函式,一旦開始執行派生類析構函式,物件內派生類的成員變數便呈現未定義值,此時物件便不完整。
為了適應這種不完整,編譯器將物件的型別視為在呼叫構造/析構函式時發生了變換,即:視物件的型別為當前建構函式/析構函式所在的類的型別。由此造成的結果是:在基類建構函式或者析構函式中,會將派生類物件當做基類型別物件對待。而這樣乙個結果,會對建構函式、析構函式呼叫期間呼叫的虛函式型別的動態繫結物件產生影響,最終的結果是:如果在建構函式或者析構函式中呼叫虛函式,執行的都將是為建構函式或者析構函式自身類型別定義的虛函式版本。
class a
~a()
};class b : public a
~b()
};int main()
上述**,最後只呼叫了父類的析構函式,沒有呼叫子類的析構函式,這在一定程度上會造成記憶體洩漏,就像你拆房子只拆了地基其他的不拆一樣。要解決這種問題可以把析構函式定義成虛函式。虛函式的呼叫會根據指標指向的型別決定,此時p指向b型別,所以會呼叫b的析構函式,而子類析構完後會自動呼叫父類析構函式,這樣就不會造成記憶體洩漏。
所以建議最好把析構函式定義成虛函式。
將建構函式定義為私有的,因為子類建立物件需要先呼叫父類的建構函式,如果父類中的建構函式被定義為私有的,那麼子類就無法呼叫父類建構函式。
class a
int _a;
};class b :public a
;
class a
private:
a(){}
int _a;
};
上面**能保證建構函式生成的物件都在堆上,但並不能保證拷貝構造出來的物件在堆上。
這個**有缺陷,雖然能保證構造的物件都是堆上的,但是不能防止拷貝構造的物件在棧上。
a* a = a::fun();//在堆上
a a1(*pa); //在棧上
此時要再優化一下**:
1. 只宣告拷貝構造不實現 。
2. 將拷貝構造宣告成私有的(防止在類外實現拷貝構造)。
class a
private:
a(){}
//將拷貝建構函式宣告成私有的
a(const a& other);
a& operator=(const a& other);
int _a;
};
class a
private:
a(){}
int _a;
};
將動態分配空間的操作符宣告為私有的,即可防止構造類物件時在堆上開空間的行為。
class a
;
要求如下:c 實現乙個
struct a
和stuct b
包含乙個int
成員_a
和_b
,要求達到b 繼承 a 的效果,也就是 b裡面包含乙個 a,並且能達到多型的效果,也就是基類指標,能根據它指向的物件型別呼叫不同的函式。
//函式指標
typedef
void(*func) ();
struct a
;struct b
;void fa()
void fb()
int main()
測試結果:
[tian@localhost test]$ ./a.out
a::fa()
b::fb()
歡迎各位大佬斧正! c 經典面試題
1 標頭檔案中的 ifndef define endif 幹什麼用?答 防止該標頭檔案被重複引用。2 include 和 include filename.h 有什麼區別?答 對於 include 編譯器從標準庫路徑開始搜尋 filename.h 對於 include filename.h 編譯器從...
C 經典面試題
1.輸入乙個字串,將其逆序後輸出。使用c 不建議用偽碼 include using namespace std void main a p malloc 1024 應該寫成 p char malloc 1024 沒有釋放p的空間,造成記憶體洩漏。8.下面的 有什麼問題?並請給出正確的寫法。void ...
C 經典面試題
最近看一些面試題,覺得如果自己被問到了,並不能很利落的回答出來。一是從來沒有這個意識,二是沒有認真的梳理下。11.引用與指標有什麼區別?參 1 引用必須被初始化,指標不必。2 引用初始化以後不能被改變,指標可以改變所指的物件。3 不存在指向空值的引用,但是存在指向空值的指標。可以參考下 在 5 題中...