日本搞逼视频_黄色一级片免费在线观看_色99久久_性明星video另类hd_欧美77_综合在线视频

國內(nèi)最全IT社區(qū)平臺 聯(lián)系我們 | 收藏本站
阿里云優(yōu)惠2
您當前位置:首頁 > 互聯(lián)網(wǎng) > 《程序員面試寶典》學習記錄7

《程序員面試寶典》學習記錄7

來源:程序員人生   發(fā)布時間:2014-09-06 19:13:06 閱讀次數(shù):2615次

印象筆記同步分享:《程序員面試寶典》學習記錄7


《程序員面試寶典》學習記錄7

第11章 繼承與接口

整個C++程序設(shè)計全面圍繞面向?qū)ο蟮姆绞竭M行。類的繼承特性是C++的一個非常重要的機制。繼承特性可以使一個新類獲得其父類的操作和數(shù)據(jù)結(jié)構(gòu),程序員只需在新類中增加原有類沒有的成分。
在面試過程中,各大企業(yè)會考量你對虛函數(shù)、純虛函數(shù)、私有繼承、多重繼承等知識點的掌握程度

11.1 覆蓋

1、以下代碼的輸出結(jié)果是什么?

#include<iostream>using namespace std; class A { protected: int m_data; public: A(int data = 0) { m_data = data; } int GetData() { return doGetData(); } virtual int doGetData() { return m_data; } }; class B : public A { protected: int m_data; public: B(int data = 1) { m_data = data; } int doGetData() { return m_data; } }; class C : public B { protected: int m_data; public: C(int data = 2) { m_data = data; } }; int main () { C c(10); cout << c.GetData() <<endl; C中未定義,故調(diào)用B中的,但是B中也未定義,故調(diào)用A中的GetData(),因為A中的doGetData()是虛函數(shù),所以調(diào)用B類中的doGetData(),而B類的doGetData()返回B::m_data, 故輸出 1cout << c.A::GetData() <<endl; 因為A中的doGetData()是虛函數(shù),所以調(diào)用B類中的doGetData(),而B類的doGetData()返回B::m_data,故輸出 1cout << c.B::GetData() <<endl; 肯定是B類的返回值 1 了。 cout << c.C::GetData() <<endl; C類中未重定義GetData(),故調(diào)用從B繼承來的GetData(),但是B類也未定義,所以調(diào)用A中的GetData(),因為A中的doGetData()是虛函數(shù),所以調(diào)用B類的doGetData(),股輸出為1 cout << c.doGetData() <<endl; B類的返回值 1 了。 cout << c.A::doGetData() <<endl; 因為直接調(diào)用了A的doGetData() ,所以輸出0cout << c.B::doGetData() <<endl; 調(diào)用了B的doGetData(),所以輸出 1cout << c.C::doGetData() <<endl; 調(diào)用了B的doGetData(),所以輸出 1return 0; }

總結(jié):這里要注意存在一個就近調(diào)用,如果父類存在相關(guān)接口則優(yōu)先調(diào)用父類接口,如果父類也不存在相關(guān)接口則調(diào)用祖父輩接口。
考點2:虛函數(shù)覆蓋虛函數(shù)

以下代碼輸出結(jié)果是什么?

#include<iostream>using namespace std; class A { public: void virtual f() { cout<<"A"<<endl; } }; class B : public A { public: void virtual f() { cout<<"B"<<endl; } }; int main () { A* pa=new A(); pa->f(); 這個很明顯A B* pb=(B*)pa; pb->f(); 這個強制將pa復制到pb,所以pb指向A delete pa,pb; 刪除pa,pb所指向的地址,但是pa、pb指針并沒有刪除,懸浮指針 pa=new B(); pa->f(); B pb=(B*)pa; pb->f(); B return 0; }

11.2 私有繼承

考點1:公有繼承和私有繼承的區(qū)別
公有繼承(public)

公有繼承的特點是基類的公有成員和保護成員作為派生類的成員時,它們都保持原有的狀態(tài),而基類的私有成員仍然是私有的,不能被這個派生類的子類所訪問。
私有繼承(private)
私有繼承的特點是基類的公有成員和保護成員都作為派生類的私有成員,并且不能被這個派生類的子類所訪問。(私有繼承使父類中的函數(shù)轉(zhuǎn)化為私有
保護繼承(protected)
保護繼承的特點是基類的所有公有成員和保護成員都成為派生類的保護成員,并且只能被它的派生類成員函數(shù)或友元訪問,基類的私有成員仍然是私有的。

public protected private 共有繼承 public protected 不可見 私有繼承 private private 不可見 保護繼承 protected protected 不可見

考點2:保護繼承和私有繼承后,子類對象想訪問父類成員
公有繼承
:子類對象可以直接訪問父類的public的成員
保護繼承:繼承之后的類相對于父類是獨立的,不能直接訪問父類成員,其類對象,在公共場合無法使用基類成員,也只能通過自己的成員函數(shù)來訪問父類的protected和public成員。
私有繼承:繼承之后也不能直接訪問父類成員,只能通過子類的成員函數(shù)來訪問父類的protected和public成員。

#include class Animal { public: Animal(){} void eat(){cout << "eat ";} }; class Giraffe:protected Animal { Giraffe(){} void StrechNeck(double) {cout << "strechneck ";} void take() { eat(); //ok } }; void main() { Giraffe girl; girl.eat(); 錯誤 保護繼承不能直接訪問父類成員 girl.take(); 正確 保護繼承只能通過子類的成員函數(shù)來訪問父類成員 girl.StretchNeck(); 正確 保護繼承只能通過子類的成員函數(shù)來訪問父類成員 }

考點3:派生類的三種繼承深入了解

#include <iostream>#include <stdio.h> class Parent { public: Parent(int var = -1) { m_nPub = var; m_nPtd = var; m_nPrt = var; } public: int m_nPub; protected: int m_nPtd; private: int m_nPrt; }; class Child1:public Parent { public: int GetPub(){return m_nPub;}; int GetPtd(){return m_nPtd;}; int GetPrt(){return m_nPrt;}; 錯誤 父類私有變量不能被子類訪問 }; class Child2:protected Parent { public: int GetPub(){return m_nPub;}; int GetPtd(){return m_nPtd;}; int GetPrt(){return m_nPrt;}; 錯誤 父類私有變量不能被子類訪問 }; class Child3:private Parent { public: int GetPub(){return m_nPub;}; int GetPtd(){return m_nPtd;}; int GetPrt(){return m_nPrt;}; 錯誤 父類私有變量不能被子類訪問 }; int main() { Child1 cd1; Child2 cd2; Child3 cd3; int nVar = 0; //公有繼承 cd1.m_nPud = nVar; 正確公有繼承訪問并改變公有變量 cd1.m_nPtd = nVar; 錯誤公有繼承m_nPtd可以被繼承訪問但是不能被修改 nVar = cd1.GetPtd(); 正確公有繼承通過函數(shù)訪問父類的公有變量 //保護繼承 cd2.m_nPtd = nVar; 錯誤 保護繼承 保護繼承不能直接訪問父類的成員 nVar = cd2.GetPtd(); 正確 保護繼承 通過函數(shù)來訪問父類成員 //私有繼承 cd3.m_nPub = nVar; 錯誤 是有繼承 不能直接修改父類的公有變量 nVar = cd3.GetPtd(); 正確 可以通過函數(shù)訪問父類的保護變量 return 0; }

11.3 虛函數(shù)繼承和虛繼承

考點1:理解虛方法(虛函數(shù))
每個對象里有虛表指針,指向虛表,虛表里存放了虛函數(shù)的地址,虛函數(shù)表是順序存放虛函數(shù)地址的,不需要用到鏈表。所以類中的每一個對象都有一個鏈表來存虛方法地址,那就是虛表。
虛函數(shù)的實現(xiàn)要求對象攜帶額外的信息,這些信息用于在運行時確定后該對象應該調(diào)用哪一個虛函數(shù),典型的情況下,這個信息具有一種被稱為vptr虛函數(shù)指針的指針形式,vptr指向一個被稱為vtbl的虛函數(shù)表函數(shù)指針數(shù)組,每一個虛函數(shù)都關(guān)聯(lián)到vtbl,當一個對象調(diào)用了虛函數(shù),實際的被調(diào)用函數(shù)通過下面步驟確定,找到對象的vptr指向的vtbl,之后在vtbl中尋找合適的函數(shù)指針。
虛擬函數(shù)使用的缺點
  優(yōu)點講了一大堆,現(xiàn)在談一下缺點,虛函數(shù)最主要的缺點是執(zhí)行效率較低,看一看虛擬函數(shù)引發(fā)的多態(tài)性的實現(xiàn)過程,你就能體會到其中的原因,另外就是由于要攜帶額外的信息(VPTR),所以導致類多占的內(nèi)存空間也會比較大,對象也是一樣的
考點2:虛函數(shù)、虛函數(shù)表、虛函數(shù)指針的聯(lián)系
每一個具有虛函數(shù)的類都有一個虛函數(shù)表VTABLE,里面按在類中聲明的虛函數(shù)的順序存放著虛函數(shù)的地址,這個虛函數(shù)表VTABLE是這個類的所有對象所共有的,也就是說無論用戶聲明了多少個類對象,但是這個VTABLE虛函數(shù)表只有一個
在每個具有虛函數(shù)的類的對象里面都有一個VPTR虛函數(shù)指針,這個指針指向VTABLE的首地址,每個類的對象都有這么一種指針。
考點3:虛函數(shù)的繼承
1)空類、單一繼承的空類、多重繼承的空類所占空間大小為:1(字節(jié),下同);
2)一個類中,虛函數(shù)本身、成員函數(shù)(包括靜態(tài)與非靜態(tài))和靜態(tài)數(shù)據(jù)成員都是不占用類對象的存儲空間的;
3)類對象的大小=各非靜態(tài)數(shù)據(jù)成員(包括父類的非靜態(tài)數(shù)據(jù)成員但都不包括所有的成員函數(shù))的總和+ vfptr指針(多繼承下可能不止一個)+vbptr指針(多繼承下可能不止一個)+編譯器額外增加的字節(jié)。
4)當類中聲明了虛函數(shù)(不管是1個還是多個),那么在實例化對象時,編譯器會自動在對象里安插一個指針vPtr指向虛函數(shù)表VTable;

#include<iostream>#include<memory.h>#include<assert.h> using namespace std; class A { char k[3]; 所占的大小為3 public: virtual void aa(){}; 虛指針大小為4 }; class B : public virtual A { char j[3]; public: virtual void bb(){}; }; class C : public virtual B { char i[3]; public: virtual void cc(){}; }; int main(int argc, char *argv[]) { cout << "sizeof(A): " << sizeof(A) << endl; 大小為4(char)+4(虛表)=8 cout << "sizeof(B): " << sizeof(B) << endl; 大小為8(A副本)+4(char)+4(虛表)=16 cout << "sizeof(C): " << sizeof(C) << endl; 大小為16(B副本)+4(char)+4(虛表)=24 return 0; }

考點3:什么是虛繼承?它和一般的繼承有什么不同?有什么用
虛擬繼承是多重繼承中特有的概念。虛擬基類是為了解決多重繼承而出現(xiàn)的,可以節(jié)省內(nèi)存空間
請看下圖:

在圖 1中,類D接觸自類B和類C,而類B和類C都繼承自類A,因此出現(xiàn)了圖 2所示的情況。

在圖 2中,類D中會出現(xiàn)兩次A。為了節(jié)省內(nèi)存空間,可以將B、C對A的繼承定義為虛擬繼承,而A成了虛擬基類。最后形成了圖 3。
代碼如下:

class A; class B : public virtual A; class C : public virtual A; class D : public B,public C;

考點4:區(qū)分虛函數(shù)繼承和虛繼承
虛擬繼承是多重繼承中特有的概念,是為解決多重繼承的。用虛繼承可以節(jié)省內(nèi)存空間
虛函數(shù)是面向?qū)ο?strong>多態(tài)性的主要方式,通過繼承基類中的虛函數(shù)在子類中重載實現(xiàn)不同操做。繼承的虛函數(shù)在子類中不需要加virtual,默認就是虛函數(shù)。可以被它的子類覆蓋。
考點4:區(qū)分虛繼承和直接繼承

#include <stdio.h> class A { public: int a; }; sizeof(A)=4 class B : virtual public A { public:   int b; sizeof(B)=4(虛表)+4(A副本)+4(自己變量)=12 }; class C : virtual public B { sizeof(c)= 12(B副本)+4(虛表) = 16 如果這里改為直接繼承,那么sizeof(c)=12 }; int main() { printf("%d ", sizeof(C)); return 0; }

再舉一個例子:

#include <stdio.h> class A { public: int a; }; sizeof(A) = 4 class B : virtual public A { }; sizeof(B) =4+4=8 class C : virtual public A { sizeof(C) =4+4=8 }; class D : public B, public C{ sizeof(D)=8+8-4=12 這里需要注意要減去4 因為B和C同時繼承A,屬于只需要保存一個A的副本就好了 sizeof(D)=4(A的副本)+4(B的虛表)+4(C的虛表)=12 }; int main() { printf("%d ", sizeof(D)); return 0; }

再舉一個例子:含有普通繼承

class A { }; class B { char ch; virtual void func0() { } }; class C { char ch1; char ch2; virtual void func() { } virtual void func1() { } }; class D: public A, public C { int d; virtual void func() { } virtual void func1() { } }; class E: public B, public C { int e; virtual void func0() { } virtual void func1() { } }; int main(void) { cout<<"A="<<sizeof(A)<<endl; result=1 空類所占空間的大小為1 cout<<"B="<<sizeof(B)<<endl; result=8 1+4 對其 8 cout<<"C="<<sizeof(C)<<endl; result=8 1+1+4 對其 8 cout<<"D="<<sizeof(D)<<endl; result=12 C的副本+D本身=12 cout<<"E="<<sizeof(E)<<endl; result=20 B的副本+C的副本+E本身=20 return 0; }

這里需要區(qū)分一下:①不沒有繼承的時候,存在虛函數(shù)則需要加上虛指針,如果有多個也只需要加上一個,因為只有一個虛指針;②對于普通繼承,類D和類E中自己的虛函數(shù),大小為0,因為他沒有虛表③對于虛繼承中,派生類中存在一個或多個虛函數(shù)的時候,它本身就有一個虛表,指向自己的虛表,所以要加4
再舉一個例子:含有虛繼承

class CommonBase { int co; 4 }; class Base1: virtual public CommonBase 4副本+4虛指針+4自身+4=16 { public: virtual void print1() { } virtual void print2() { } private: int b1; }; class Base2: virtual public CommonBase 同理16 { public: virtual void dump1() { } virtual void dump2() { } private: int b2; }; class Derived: public Base1, public Base2 16+16-4+4=32 { public: void print2() { } void dump2() { } private: int d; };

class Derived size(32):

+--- | +--- (base class Base1) | | {vfptr} | | {vbptr} | | b1 | +--- | +--- (base class Base2) | | {vfptr} | | {vbptr} | | b2 | +--- | d +--- +--- (virtual base CommonBase) | co +---

再舉一個例子:

class A { public: virtual void aa() { } virtual void aa2() { } 生活不易,碼農(nóng)辛苦
如果您覺得本網(wǎng)站對您的學習有所幫助,可以手機掃描二維碼進行捐贈
程序員人生
------分隔線----------------------------
分享到:
------分隔線----------------------------
關(guān)閉
程序員人生
主站蜘蛛池模板: www.久久久| 亚洲一区免费观看 | 亚洲免费在线观看视频 | 欧美三级网站 | 激情自拍视频 | 国产精品一区二区三区久久 | 久久精品国产清自在天天线 | 午夜午夜精品一区二区三区文 | 久久久久亚洲综合 | 国产传媒一区二区三区 | 免费观看污污视频 | 91久久久久久久久 | 久久久久久久一区二区三区 | 国产在线一二三区 | 国产精品久久久久久久久久免费 | 国产午夜精品一区二区三区四区 | 色婷婷综合久久久中字幕精品久久 | 日韩一区二区三区精品视频 | 人妖啪啪综合av一区ts人妖 | 天堂a在线| 久久久888 | 曰韩av | 成人网av | 视频一区二区三区中文字幕 | www.狠狠干| 国产精品第二页 | 国产在线日韩 | 欧美日韩精品一区二区三区蜜桃 | 国产福利在线观看 | 成人在线视频看看 | 欧美日韩不卡 | 亚洲天堂一区二区 | 欧美二三区| 日韩欧乱色一区二区三区在线 | 亚洲h | 国产成人精品一区二区在线 | 久久熟| 久久精品国产久精国产 | 国产剧情一区二区三区 | 日本一二三区视频在线 | 日本欧美在线 |