一. sizeof計算結構體
注:本機機器字長為64位
1.最普通的類和普通的繼承
#include<iostream>
using namespace std;class Parent{
public:void fun(){cout<<"Parent fun"<<endl;}
}; class Child : public Parent{
public:void fun(){cout<<"Child fun"<<endl;}char ch[5];
};int main(){Parent p;Child ch;cout<<"Parent size : "<<sizeof(p)<<", Child size : "<<sizeof(ch) <<endl;return 0;
}/*運行結果:Parent size : 1, Child size : 5
*/
分析:那么為什么類(對象)的大小為什么會是1個字節(jié)呢?那是被編譯器插進去的一個char ,使得這個class的不同實體(object)在內存中配置獨一無二的地址。也就是說這個char是用來標識類的不同對象的。因為如果不是1,當定義這個類的對象數(shù)組時候A objects[5]; objects[0]和objects[1]就在同一個地址處,就無法區(qū)分。
2.基類中含有私有成員
#include<iostream>
using namespace std;class Parent{
public:void fun(){cout<<"Parent fun"<<endl;}
private:int x;
}; class Child : public Parent{
public:void fun(){cout<<"Child fun"<<endl;}char ch[5];
};int main(){Parent p;Child ch;cout<<"Parent size : "<<sizeof(p)<<", Child size : "<<sizeof(ch) <<endl;return 0;
}
/*執(zhí)行結果:Parent size : 4, Child size : 12
*/
分析:基類里的私有成員在派生類里仍占有內存。在派生類里,基類的int占4個字節(jié),char ch[5]占用5個字節(jié),考慮內存的對齊,變成4+(5+3)=12個字節(jié)。
?
3.類中含有虛函數(shù)
#include<iostream>
using namespace std;class Parent{
public:virtual void fun(){cout<<"Parent fun"<<endl;}
}; class Child : public Parent{
public:virtual void hjzgg(){cout<<"呵呵"<<endl;}char ch[5];
};int main(){Parent p;Child ch;cout<<"Parent size : "<<sizeof(p)<<", Child size : "<<sizeof(ch) <<endl;return 0;
}
/*執(zhí)行結果:Parent size : 8, Child size : 16
*/
分析:有虛函數(shù)的類有個virtual table(虛函數(shù)表),里面包含了類的所有虛函數(shù),類中有個virtual table pointers,通常成為vptr指向這個virtual table,占用8個字節(jié)的大小。成員類Child public繼承于Parent,類Child的虛函數(shù)表里實際上有兩個虛函數(shù)Parent::fun()和Child::hjzgg(),類B的大小等于char ch[5]的大小加上一個指向虛函數(shù)表指針vptr的大小,考慮內存對齊為16。一個類里若有虛函數(shù),無論有多少個虛函數(shù)都只有一個指向虛表的指針,虛表中的每一個表項保存著一個虛函數(shù)的入口地址。當調用虛函數(shù)時,先找到虛表中它對應的表項,找到入口地址再執(zhí)行。
4.多重繼承
#include<iostream>
using namespace std;class Parent{
public:virtual void fun(){cout<<"Parent fun"<<endl;}
}; class Father{
public:virtual void fun(){cout<<"Father fun"<<endl;}
};class Child : public Parent, public Father{
public:virtual void hjzgg(){cout<<"呵呵"<<endl;}char ch[5];
};int main(){Parent p;Child ch;cout<<"Parent size : "<<sizeof(p)<<", Child size : "<<sizeof(ch) <<endl;return 0;
}
/*執(zhí)行結果:Parent size : 8, Child size : 24
*/
分析:Child中除了char ch[5]這5個字節(jié),Child現(xiàn)有一個虛函數(shù)表,里邊有Child自身定義的虛函數(shù)以及從Parent中繼承過來的虛函數(shù),然后又另一張?zhí)摵瘮?shù)表來存放Father中過來的虛函數(shù),也就是Child對應兩個虛函數(shù)表的指針??偣矁却婵臻g5+8+8=21,考慮內存的對齊,為24字節(jié)。
?5.虛繼承
C++虛擬繼承
◇概念:
? C++使用虛擬繼承(Virtual Inheritance),解決從不同途徑繼承來的同名的數(shù)據(jù)成員在內存中有不同的拷貝造成數(shù)據(jù)不一致問題,將共同基類設置為虛基類。 ? ? ? ? ?這時從不同的路徑繼承過來的同名數(shù)據(jù)成員在內存中就只有一個拷貝,同一個函數(shù)名也只有一個映射。
◇解決問題:
解決了二義性問題,也節(jié)省了內存,避免了數(shù)據(jù)不一致的問題。? ◇同義詞: 虛基類(把一個動詞當成一個名詞而已) 當在多條繼承路徑上有一個公共的基類,在這些路徑中的某幾條匯合處,這個公共的基類就會產生多個實例(或多個副本),若只想保存這個基類的一個實例,可以將這個 公共基類說明為虛基類。 ◇執(zhí)行順序
??首先執(zhí)行虛基類的構造函數(shù),多個虛基類的構造函數(shù)按照被繼承的順序構造;
執(zhí)行基類的構造函數(shù),多個基類的構造函數(shù)按照被繼承的順序構造;
執(zhí)行成員對象的構造函數(shù),多個成員對象的構造函數(shù)按照申明的順序構造;
執(zhí)行派生類自己的構造函數(shù);
??析構以與構造相反的順序執(zhí)行;
注:
?從虛基類直接或間接派生的派生類中的構造函數(shù)的成員初始化列表中都要列出對虛基類構造函數(shù)的調用。但只有用于建立對象的最派生類的構造函數(shù)調用虛基類 ? ? ? ? ? 的構造函數(shù),而該派生類的所有基類中列出的對虛基類的構造函數(shù)的調用在執(zhí)行中被忽略,從而保證對虛基類子對象只初始化一次。
?在一個成員初始化列表中同時出現(xiàn)對虛基類和非虛基類構造函數(shù)的調用時,虛基類的構造函數(shù)先于非虛基類的構造函數(shù)執(zhí)行。
?5.1?沒有用虛擬繼承
#include<iostream>
using namespace std;class Parent{
public:virtual void fun(){cout<<"Parent fun"<<endl;}
}; class Father{
public:virtual void fun(){cout<<"Father fun"<<endl;}
};class Child : public Parent, public Father{
public:virtual void hjzgg(){cout<<"呵呵"<<endl;}char ch[5];
};int main(){Parent p;Child ch;ch.fun();ch.Parent::fun();//這樣調用是對的ch.Father::fun();cout<<"Parent size : "<<sizeof(p)<<", Child size : "<<sizeof(ch) <<endl;return 0;
}
/*執(zhí)行結果:[Error] request for member 'fun' is ambiguous
*/
分析:因為派生類中的虛函數(shù)表會繼承來自各個基類的虛函數(shù)。所以Child對應的虛函數(shù)表中會有Parent 和 Father各自的fun()函數(shù),所以在調用的時候就會出現(xiàn)歧義,不知道應該調用哪個!
同樣,和下面一樣的寫法也是錯誤的,增加一個Super類。
#include<iostream>
using namespace std;class Super{
public:virtual void fun(){cout<<"Super fun"<<endl;}
};class Parent : public Super{
public:}; class Father : public Super{
public:};class Child : public Parent, public Father{
public:virtual void hjzgg(){cout<<"呵呵"<<endl;}char ch[5];
};int main(){Parent p;Child ch;ch.fun();cout<<"Parent size : "<<sizeof(p)<<", Child size : "<<sizeof(ch) <<endl;return 0;
}/*執(zhí)行結果:[Error] request for member 'fun' is ambiguous
*/
5.2 使用虛擬繼承#include<iostream>?
using namespace std;class Super{
public:Super(){cout<<"Super construction"<<endl;}virtual void fun(){cout<<"Super fun"<<endl;}
};class Parent : virtual public Super{
public:Parent(){cout<<"Parent construction"<<endl;}
}; class Father : virtual public Super{
public:Father(){cout<<"Father construction"<<endl;}
};class Child : public Parent, public Father{
public:virtual void hjzgg(){cout<<"呵呵"<<endl;}char ch[5];
};int main(){Child ch;ch.fun();cout<<"Parent size : "<<sizeof(Parent)<<", Child size : "<<sizeof(Child) <<endl;return 0;
}
/*執(zhí)行結果:Super constructionParent constructionFather constructionSuper funParent size : 8, Child size : 24*/
分析:
1.在多繼承情況下,虛基類關鍵字的作用范圍和繼承方式關鍵字相同,只對緊跟其后的基類起作用。 2.
聲明了虛基類之后,虛基類在進一步派生過程中始終和派生類一起,維護同一個基類子對象的拷貝。(Super的構造函數(shù)只執(zhí)行了一次,如果不是有虛基類,那么Super的構造函數(shù) 將會執(zhí)行兩次。) 3.觀察類構造函數(shù)的構造順序,拷貝也只有一份。
二. c++重載、覆蓋、隱藏的區(qū)別和執(zhí)行方式
1.成員函數(shù)被重載的特征
(1)相同的范圍(在同一個類中);?
(2)函數(shù)名字相同;?
(3)參數(shù)不同;?
(4)virtual 關鍵字可有可無。?
2.“覆蓋”是指派生類函數(shù)覆蓋基類函數(shù),特征是:
(1)不同的范圍(分別位于派生類與基類);?
(2)函數(shù)名字相同;?
(3)參數(shù)相同;?
(4)基類函數(shù)必須有virtual 關鍵字。?
3.“隱藏”是指派生類的函數(shù)屏蔽了與其同名的基類函數(shù),特征是:
(1)不同的范圍(分別位于派生類與基類);
(2)如果派生類的函數(shù)與基類的函數(shù)同名,但是參數(shù)不同,此時,不論有無virtual關鍵字,基類的函數(shù)將被隱藏(注意別與重載混淆)。?
(3)如果派生類的函數(shù)與基類的函數(shù)同名,且參數(shù)相同,但是基類函數(shù)沒有virtual 關鍵字。此時,基類的函數(shù)被隱藏(注意別與覆蓋混淆)。
小結:說白了就是如果派生類和基類的函數(shù)名和參數(shù)都相同,屬于覆蓋,這是可以理解的吧,完全一樣當然要覆蓋了;如果只是函數(shù)名相同,參數(shù)并不相同,則屬 于隱藏。
總結
以上是生活随笔為你收集整理的c++面试常用知识(sizeof计算类的大小,虚拟继承,重载,隐藏,覆盖)的全部內容,希望文章能夠幫你解決所遇到的問題。
如果覺得生活随笔網(wǎng)站內容還不錯,歡迎將生活随笔推薦給好友。