虛函數(shù)的override說明符
派生類經(jīng)常(但不總是)重寫它們繼承的虛函數(shù)。如果派生類沒有重寫其基類中的虛函數(shù)鳖轰,那么與任何其他成員一樣蕴侣,派生類繼承其基類中定義的版本臭觉。
派生類可以在其重寫的函數(shù)上包含關(guān)鍵字virtual
,但編譯器并沒有對(duì)此進(jìn)行強(qiáng)制要求狞膘。新標(biāo)準(zhǔn)允許派生類明確指出它希望成員函數(shù)重寫它繼承的虛函數(shù)挽封。通過在參數(shù)列表之后指定override
。如果成員是const
或者引用和悦,則在const
或引用限定符之后指定override
鸽素。
Code:
class Bulk_quote : public Quote { // Bulk_quote inherits from Quote
Bulk_quote() = default;
Bulk_quote(const std::string&, double, std::size_t, double);
// overrides the base version of function 'net_price'
double net_price(std::size_t) const override;
private:
std::size_t min_qty = 0; // minimum purchase for the discount to apply
double discount = 0.0; // fractional discount to apply
};
通過將類定義為final來阻止繼承
有時(shí)我們定義一個(gè)我們不希望其他人繼承的類亦鳞⊙嗖睿或者我們可以定義一個(gè)我們不想考慮它是否適合作為基類的類。根據(jù)新標(biāo)準(zhǔn)瓦呼,我們可以通過在類名之后使用關(guān)鍵字final
來阻止類被用作基類:
Code:
class NoDerived final { /* */ }; // NoDerived can't be a base class
class Base { /* */ };
// Last is final; we cannot inherit from Last
class Last final : Base { /* */ }; // Last can't be a base class
class Bad : NoDerived { /* */ }; // error: NoDerived is final
class Bad2 : Last { /* */ }; // error: Last is final
虛函數(shù)的override和final說明符
派生類定義一個(gè)與其基類中的虛擬同名但具有不同參數(shù)列表的函數(shù)是合法的央串。編譯器認(rèn)為這樣的函數(shù)獨(dú)立于基類函數(shù)碗啄。在這種情況下稚字,派生版本不會(huì)重寫基類中的版本。在實(shí)踐中瘫想,這樣的聲明通常是一個(gè)錯(cuò)誤昌讲,因?yàn)轭愖髡叽蛩銖幕愔貙懱摵瘮?shù)但在指定參數(shù)列表時(shí)出錯(cuò)剧蚣。
在新標(biāo)準(zhǔn)下,我們可以在派生類中的虛函數(shù)上指定override
礼搁。這樣做可以讓我們的意圖變得清晰目尖,并且(更重要的是)讓編譯器為我們找到這些問題瑟曲。如果標(biāo)記為override
的函數(shù)沒有重寫現(xiàn)有的虛函數(shù),編譯器將拒絕該程序:
Code:
struct B {
virtual void f1(int) const;
virtual void f2();
void f3();
};
struct D1 : B {
void f1(int) const override; // ok: f1 matches f1 in the base
void f2(int) override; // error: B has no f2(int) function
void f3() override; // error: f3 not virtual
void f4() override; // error: B doesn't have a function named f4
};
在D1
中扯罐,對(duì)f1
指定override
說明符是合法的歹河』ㄒ鳎基類和派生類中的f1
都是const
型成員衅澈,同時(shí)接收一個(gè)int
參數(shù),返回類型為void
经备。D1
中的f1
正確重寫了繼承自B
的虛函數(shù)弄喘。
D1
中聲明的f2
沒有匹配B
中的f2
聲明甩牺,因?yàn)?code>B中定義的版本沒有參數(shù)而D1
中定義的版本接收一個(gè)int
參數(shù)贬派。因?yàn)槁暶鞑黄ヅ洌?code>D1中的f2
沒有重寫B
中的f2
波桩;它是一個(gè)新的函數(shù)请敦,這個(gè)函數(shù)剛好重名。因?yàn)槲覀兺ㄟ^關(guān)鍵字override
說明這是一個(gè)重寫基類中虛函數(shù)的成員函數(shù)撒穷,但是實(shí)際上它并沒有重寫裆熙,所以編譯器會(huì)產(chǎn)生一個(gè)錯(cuò)誤入录。
因?yàn)閮H有虛函數(shù)才能被重寫,編譯器也將拒絕D1
中的f3
凡桥,因?yàn)樵?code>B中這個(gè)函數(shù)不是虛函數(shù)蚀同,所以不能對(duì)其進(jìn)行重寫唤崭。同樣,f4
也將產(chǎn)生一個(gè)錯(cuò)誤因?yàn)?code>B中沒有名字為f4
的函數(shù)腕侄。
我們也可以指定一個(gè)函數(shù)為final
冕杠。任何嘗試重寫聲明為final
的函數(shù)都將被標(biāo)記為一個(gè)錯(cuò)誤:
Code:
struct D2 : B {
// inherits f2() and f3() from B and overrides f1(int)
void f1(int) const final; // subsequent classes can't override f1 (int)
};
struct D3 : D2 {
void f2(); // ok: overrides f2 inherited from the indirect base, B
void f1(int) const; // error: D2 declared f2 as final
};
final
和override
說明符出現(xiàn)在參數(shù)列表(包括任何const
或引用限定符)之后和尾隨返回(trailing return type
)之后分预。
刪除的拷貝控制和繼承
合成的默認(rèn)構(gòu)造函數(shù)或基類或派生類的任何拷貝控制成員可以被定義為已刪除薪捍。此外,定義基類的方式可能導(dǎo)致派生類成員被定義為已刪除:
- 如果基類中的默認(rèn)構(gòu)造函數(shù)凳干,拷貝構(gòu)造函數(shù)救赐,拷貝賦值運(yùn)算符或析構(gòu)函數(shù)被刪除或不可訪問只磷,則派生類中的相應(yīng)成員被定義為已刪除,因?yàn)榫幾g器無法使用用于構(gòu)造阿迈,賦值或銷毀對(duì)象的基類部分的基類成員配乓。
- 如果基類具有不可訪問或刪除的析構(gòu)函數(shù)犹芹,則派生類中的合成默認(rèn)構(gòu)造函數(shù)和拷貝構(gòu)造函數(shù)將被定義為已刪除鞠绰,因?yàn)闊o法銷毀派生對(duì)象的基部分。
- 像往常一樣蜈膨,編譯器不會(huì)合成已刪除的
move
操作屿笼。如果我們使用= default
來請(qǐng)求move
操作,那么如果基類中的相應(yīng)操作被刪除或不可訪問翁巍,它將是派生類中的已刪除函數(shù)驴一,因?yàn)榛惒糠譄o法移動(dòng)。如果基類析構(gòu)函數(shù)被刪除或無法訪問灶壶,也將刪除移動(dòng)構(gòu)造函數(shù)肝断。
Code:
class B {
public:
B();
B(const B&) = delete;
// other members, not including a move constructor
};
class D : public B {
// no constructors
};
D d; // ok: D's synthesized default constructor uses B's default constructor
D d2(d); // error: D's synthesized copy constructor is deleted
D d3(std::move(d)); // error: implicitly uses D's deleted copy constructor
例如,此基類B具有可訪問的默認(rèn)構(gòu)造函數(shù)和顯式刪除的拷貝構(gòu)造函數(shù)驰凛。因?yàn)槎x了拷貝構(gòu)造函數(shù)胸懈,所以編譯器不會(huì)為B類合成移動(dòng)構(gòu)造函數(shù)恰响。因此趣钱,我們既不能移動(dòng)也不能復(fù)制B類對(duì)象。如果從B派生的類想要允許復(fù)制或移動(dòng)其對(duì)象胚宦,那么派生類必須定義這些構(gòu)造函數(shù)的自己版本首有。當(dāng)然,該類必須決定如何復(fù)制或移動(dòng)其基類部分中的成員枢劝。實(shí)際上绞灼,如果基類沒有默認(rèn),復(fù)制或移動(dòng)構(gòu)造函數(shù)呈野,那么它的派生類通常也不會(huì)有低矮。
繼承的構(gòu)造函數(shù)
在新標(biāo)準(zhǔn)下,派生類可以重用由其直接基類定義的構(gòu)造函數(shù)被冒。類可以僅初始化其直接基類军掂, 類可以僅從其直接基類繼承構(gòu)造函數(shù)轮蜕。類不能繼承默認(rèn),拷貝和移動(dòng)構(gòu)造函數(shù)蝗锥。如果派生類沒有直接定義這些構(gòu)造函數(shù)跃洛,則編譯器會(huì)像往常一樣合成它們。
派生類通過使用using聲明來繼承其基類構(gòu)造函數(shù)终议。作為示例汇竭,我們可以定義Bulk_quote
類并繼承Disc_quote
類的構(gòu)造函數(shù):
Code:
class Bulk_quote : public Disc_quote {
public:
using Disc_quote::Disc_quote; // inherit Disc_quote's constructors
double net_price(std::size_t) const;
};
通常,using
聲明僅在當(dāng)前作用域中使名稱可見穴张。當(dāng)應(yīng)用于構(gòu)造函數(shù)時(shí)细燎,using
聲明會(huì)導(dǎo)致編譯器生成代碼。編譯器生成與基類中的各個(gè)構(gòu)造函數(shù)對(duì)應(yīng)的派生構(gòu)造函數(shù)皂甘。也就是說玻驻,對(duì)于基類中的每個(gè)構(gòu)造函數(shù),編譯器在派生類中生成具有相同參數(shù)列表的構(gòu)造函數(shù)偿枕。
這些由編譯器產(chǎn)生的構(gòu)造函數(shù)有如下形式:
derived(parms) : base(args) { }
其中derived
是派生類的名稱璧瞬,base
是基類的名稱,parms
是構(gòu)造函數(shù)的參數(shù)列表渐夸,args
將派生構(gòu)造函數(shù)中的參數(shù)傳遞給基礎(chǔ)構(gòu)造函數(shù)嗤锉。在我們的Bulk_quote
類中,繼承的構(gòu)造函數(shù)等價(jià)于:
Code:
Bulk_quote(const std::string& book, double price,
std::size_t qty, double disc):
Disc_quote(book, price, qty, disc) { }
如果派生類具有自己的任何數(shù)據(jù)成員墓塌,則默認(rèn)初始化這些成員档冬。
參考文獻(xiàn)
[1] Lippman S B , Josée Lajoie, Moo B E . C++ Primer (5th Edition)[J]. 2013.