<em>Mac</em>Book项目 2009年学校开始实施<em>Mac</em>Book项目,所有师生配备一本<em>Mac</em>Book,并同步更新了校园无线网络。学校每周进行电脑技术更新,每月发送技术支持资料,极大改变了教学及学习方式。因此2011
2021-06-01 09:32:01
公有繼承包含兩部分:一是"函數介面" (interface),二是"函數實現" (implementation)
如 Shape 類中,三個成員函數,對應三種繼承方式:
class Shape { public: virtual void Draw() const = 0; // 1) 純虛擬函式 virtual void Error(const string& msg); // 2) 普通虛擬函式 int ObjectID() const; // 3) 非虛擬函式 }; class Rectangle: public Shape { ... }; class Ellipse: public Shape { ... };
Shape *ps1 = new Rectangle; ps1->Draw(); // calls Rectangle::Draw Shape *ps2 = new Ellipse; ps2->Draw(); // calls Ellipse::Draw
純虛擬函式,繼承的是基礎類別中,成員函數的介面,且要在派生類中,重寫成員函數的實現
呼叫基礎類別的 Draw(),須加 類作用域操作符 ::
ps1->Shape::Draw(); // calls Shape::draw
普通虛擬函式,會在基礎類別中,定義一個預設的實現 (default implementation),表示繼承的是基礎類別成員函數介面和預設實現,由派生類選擇是否重寫該函數。
實際上,允許普通虛擬函式 同時繼承介面和預設實現是危險的。如下, ModelA 和 ModelB 是 Airplane 的兩種飛機型別,且二者的飛行方式完全相同
class Airplane { public: virtual void Fly(const string& destination); }; class ModelA: public Airplane { ... }; class ModelB: public Airplane { ... };
這是典型的物件導向設計,兩個類共用一個特性 -- Fly,則 Fly 可在基礎類別中實現,並由兩個派生類繼承之
現增加另一個飛機型號 ModelC,其飛行方式與 ModelA,ModelB 不相同,如果不小心忘記在 ModelC 中重寫新的 Fly 函數
class ModelC: public Airplane { ... // no fly function is declared };
則呼叫 ModelC 中的 fly 函數,就是呼叫 Airplane::Fly,但是 ModelC 的飛行方式和預設的並不相同
Airplane *pa = new ModelC; pa->Fly(Qingdao); // calls Airplane::fly!
即前面所說的,普通虛擬函式同時繼承介面和預設實現是危險的,最好是基礎類別中實現預設行為 (behavior),但只有在派生類要求時才提供該預設行為
一種方法是 純虛擬函式 + 預設實現,因為是純虛擬函式,所以只有介面被繼承,其預設的實現不會被繼承。派生類要想使用該預設的實現,必須顯式的呼叫
class Airplane { public: virtual void Fly(const string& destination) = 0; }; void Airplane::Fly(const string& destination) { // a pure virtual function default code for flying an airplane to the given destination } class ModelA: public Airplane { virtual void Fly(const string& destination) { Airplane::Fly(destination); } };
這樣在派生類 ModelC 中,即使一不小心忘記重寫 Fly 函數,也不會呼叫 Airplane 的預設實現
class ModelC: public Airplane { public: virtual void Fly(const string& destination); }; void ModelC::Fly(const string& destination) { // code for flying a ModelC airplane to the given destination }
可以看到,上面問題的關鍵就在於,一不小心在派生類 ModelC 中忘記重寫 fly 函數,C++11 中使用關鍵字 override,可以避免這樣的“一不小心”
非虛成員函數沒有 virtual 關鍵字,表示派生類不但繼承了介面,而且繼承了一個強制實現 (mandatory implementation)
既然繼承了一個強制的實現,則在派生類中,無須重新定義 (redefine) 繼承自基礎類別的成員函數,如下:
使用指標呼叫 ObjectID 函數,則都是呼叫的 Shape::ObjectID()
Rectangel rc; // rc is an object of type Rectangle Shape *pB = &rc; // get pointer to rc pB->ObjectID(); // call ObjectID() through pointer Rectangle *pD = &rc; // get pointer to rc pD->ObjectID(); // call ObjectID() through pointer
如果在派生類中重新定義了繼承自基礎類別的成員函數 ObjectID 呢?
class Rectangel : public Shape { public: int ObjectID() const; // hides Shape::ObjectID }; pB->ObjectID(); // calls Shape::ObjectID() pD->ObjectID(); // calls Rectagle::ObjectID()
此時,派生類中重新定義的成員函數會 “隱藏” (hide) 繼承自基礎類別的成員函數
這是因為非虛擬函式是 “靜態繫結” 的,pB 被宣告的是 Shape* 型別的指標,則通過 pB 呼叫的非虛擬函式都是基礎類別中的,既使 pB 指向的是派生類
與“靜態繫結”相對的是虛擬函式的“動態繫結”,即無論 pB 被宣告為 Shape* 還是 Rectangle* 型別,其呼叫的虛擬函式取決於 pB 實際指向的物件型別
在 1.2.2 中提到 override 關鍵字,可以避免派生類中忘記重寫虛擬函式的錯誤
下面以重寫虛擬函式時,容易犯的四個錯誤為例,詳細闡述之
class Base { public: virtual void mf1() const; virtual void mf2(int x); virtual void mf3() &; void mf4() const; // is not declared virtual in Base }; class Derived: public Base { public: virtual void mf1(); // declared const in Base, but not in Derived. virtual void mf2(unsigned int x); // takes an int in Base, but an unsigned int in Derived virtual void mf3() &&; // is lvalue-qualified in Base, but rvalue-qualified in Derived. void mf4() const; };
在派生類中,重寫 (override) 繼承自基礎類別成員函數的實現 (implementation) 時,要滿足如下條件:
一虛:基礎類別中,成員函數宣告為虛擬的 (virtual)
二容:基礎類別和派生類中,成員函數的返回型別和異常規格 (exception specification) 必須相容
四同:基礎類別和派生類中,成員函數名、形參型別、常數屬性 (constness) 和 參照限定符 (reference qualifier) 必須完全相同
如此多的限制條件,導致了虛擬函式重寫如上述程式碼,極容易因為一個不小心而出錯
C++11 中的 override 關鍵字,可以顯式的在派生類中宣告,哪些成員函數需要被重寫,如果沒被重寫,則編譯器會報錯。
class Derived: public Base { public: virtual void mf1() override; virtual void mf2(unsigned int x) override; virtual void mf3() && override; virtual void mf4() const override; };
因此,即使不小心漏寫了虛擬函式重寫的某個苛刻條件,也可以通過編譯器的報錯,快速改正錯誤
class Derived: public Base { public: virtual void mf1() const override; // adding "virtual" is OK, but not necessary virtual void mf2(int x) override; void mf3() & override; void mf4() const override; };
1) 公有繼承
純虛擬函式 => 繼承的是:介面 (interface)
普通虛擬函式 => 繼承的是:介面 + 預設實現 (default implementation)
非虛成員函數 =>繼承的是:介面 + 強制實現 (mandatory implementation)
2) 不要重新定義一個繼承自基礎類別的非虛擬函式 (never redefine an inherited non-virtual function)
3) 在宣告需要重寫的函數後,加關鍵字 override
《Effective C++》3rd,item 34, item 36
《Effective Modern C++》 item 12
到此這篇關於C++11 中的override詳解的文章就介紹到這了,更多相關C++11 override內容請搜尋it145.com以前的文章或繼續瀏覽下面的相關文章希望大家以後多多支援it145.com!
相關文章
<em>Mac</em>Book项目 2009年学校开始实施<em>Mac</em>Book项目,所有师生配备一本<em>Mac</em>Book,并同步更新了校园无线网络。学校每周进行电脑技术更新,每月发送技术支持资料,极大改变了教学及学习方式。因此2011
2021-06-01 09:32:01
综合看Anker超能充系列的性价比很高,并且与不仅和iPhone12/苹果<em>Mac</em>Book很配,而且适合多设备充电需求的日常使用或差旅场景,不管是安卓还是Switch同样也能用得上它,希望这次分享能给准备购入充电器的小伙伴们有所
2021-06-01 09:31:42
除了L4WUDU与吴亦凡已经多次共事,成为了明面上的厂牌成员,吴亦凡还曾带领20XXCLUB全队参加2020年的一场音乐节,这也是20XXCLUB首次全员合照,王嗣尧Turbo、陈彦希Regi、<em>Mac</em> Ova Seas、林渝植等人全部出场。然而让
2021-06-01 09:31:34
目前应用IPFS的机构:1 谷歌<em>浏览器</em>支持IPFS分布式协议 2 万维网 (历史档案博物馆)数据库 3 火狐<em>浏览器</em>支持 IPFS分布式协议 4 EOS 等数字货币数据存储 5 美国国会图书馆,历史资料永久保存在 IPFS 6 加
2021-06-01 09:31:24
开拓者的车机是兼容苹果和<em>安卓</em>,虽然我不怎么用,但确实兼顾了我家人的很多需求:副驾的门板还配有解锁开关,有的时候老婆开车,下车的时候偶尔会忘记解锁,我在副驾驶可以自己开门:第二排设计很好,不仅配置了一个很大的
2021-06-01 09:30:48
不仅是<em>安卓</em>手机,苹果手机的降价力度也是前所未有了,iPhone12也“跳水价”了,发布价是6799元,如今已经跌至5308元,降价幅度超过1400元,最新定价确认了。iPhone12是苹果首款5G手机,同时也是全球首款5nm芯片的智能机,它
2021-06-01 09:30:45