C++11——面向对象编程

虚函数的override说明符

派生类经常(但不总是)重写它们继承的虚函数。如果派生类没有重写其基类中的虚函数,那么与任何其他成员一样,派生类继承其基类中定义的版本。
派生类可以在其重写的函数上包含关键字virtual,但编译器并没有对此进行强制要求。新标准允许派生类明确指出它希望成员函数重写它继承的虚函数。通过在参数列表之后指定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来阻止继承

有时我们定义一个我们不希望其他人继承的类。或者我们可以定义一个我们不想考虑它是否适合作为基类的类。根据新标准,我们可以通过在类名之后使用关键字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

虚函数的override和final说明符

派生类定义一个与其基类中的虚拟同名但具有不同参数列表的函数是合法的。编译器认为这样的函数独立于基类函数。在这种情况下,派生版本不会重写基类中的版本。在实践中,这样的声明通常是一个错误,因为类作者打算从基类重写虚函数但在指定参数列表时出错。
在新标准下,我们可以在派生类中的虚函数上指定override。这样做可以让我们的意图变得清晰,并且(更重要的是)让编译器为我们找到这些问题。如果标记为override的函数没有重写现有的虚函数,编译器将拒绝该程序:

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中,对f1指定override说明符是合法的。基类和派生类中的f1都是const型成员,同时接收一个int参数,返回类型为voidD1中的f1正确重写了继承自B的虚函数。
D1中声明的f2没有匹配B中的f2声明,因为B中定义的版本没有参数而D1中定义的版本接收一个int参数。因为声明不匹配,所以D1中的f2没有重写B中的f2;它是一个新的函数,这个函数刚好重名。因为我们通过关键字override说明这是一个重写基类中虚函数的成员函数,但是实际上它并没有重写,所以编译器会产生一个错误。
因为仅有虚函数才能被重写,编译器也将拒绝D1中的f3,因为在B中这个函数不是虚函数,所以不能对其进行重写。同样,f4也将产生一个错误因为B中没有名字为f4的函数。
我们也可以指定一个函数为final。任何尝试重写声明为final的函数都将被标记为一个错误:

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
    };

finaloverride说明符出现在参数列表(包括任何const或引用限定符)之后和尾随返回(trailing return type)之后。

删除的拷贝控制和继承

合成的默认构造函数或基类或派生类的任何拷贝控制成员可以被定义为已删除。此外,定义基类的方式可能导致派生类成员被定义为已删除:

  1. 如果基类中的默认构造函数,拷贝构造函数,拷贝赋值运算符或析构函数被删除或不可访问,则派生类中的相应成员被定义为已删除,因为编译器无法使用用于构造,赋值或销毁对象的基类部分的基类成员。
  2. 如果基类具有不可访问或删除的析构函数,则派生类中的合成默认构造函数和拷贝构造函数将被定义为已删除,因为无法销毁派生对象的基部分。
  3. 像往常一样,编译器不会合成已删除的move操作。如果我们使用= default来请求move操作,那么如果基类中的相应操作被删除或不可访问,它将是派生类中的已删除函数,因为基类部分无法移动。如果基类析构函数被删除或无法访问,也将删除移动构造函数。
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具有可访问的默认构造函数和显式删除的拷贝构造函数。因为定义了拷贝构造函数,所以编译器不会为B类合成移动构造函数。因此,我们既不能移动也不能复制B类对象。如果从B派生的类想要允许复制或移动其对象,那么派生类必须定义这些构造函数的自己版本。当然,该类必须决定如何复制或移动其基类部分中的成员。实际上,如果基类没有默认,复制或移动构造函数,那么它的派生类通常也不会有。

继承的构造函数

在新标准下,派生类可以重用由其直接基类定义的构造函数。类可以仅初始化其直接基类, 类可以仅从其直接基类继承构造函数。类不能继承默认,拷贝和移动构造函数。如果派生类没有直接定义这些构造函数,则编译器会像往常一样合成它们。
派生类通过使用using声明来继承其基类构造函数。作为示例,我们可以定义Bulk_quote类并继承Disc_quote类的构造函数:

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声明仅在当前作用域中使名称可见。当应用于构造函数时,using声明会导致编译器生成代码。编译器生成与基类中的各个构造函数对应的派生构造函数。也就是说,对于基类中的每个构造函数,编译器在派生类中生成具有相同参数列表的构造函数。
这些由编译器产生的构造函数有如下形式:

    derived(parms) : base(args) { }

其中derived是派生类的名称,base是基类的名称,parms是构造函数的参数列表,args将派生构造函数中的参数传递给基础构造函数。在我们的Bulk_quote类中,继承的构造函数等价于:

Code:
    Bulk_quote(const std::string& book, double price,
                std::size_t qty, double disc):
           Disc_quote(book, price, qty, disc) { }

如果派生类具有自己的任何数据成员,则默认初始化这些成员。

参考文献

[1] Lippman S B , Josée Lajoie, Moo B E . C++ Primer (5th Edition)[J]. 2013.

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 212,029评论 6 492
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 90,395评论 3 385
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 157,570评论 0 348
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 56,535评论 1 284
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 65,650评论 6 386
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 49,850评论 1 290
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,006评论 3 408
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 37,747评论 0 268
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,207评论 1 303
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 36,536评论 2 327
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 38,683评论 1 341
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,342评论 4 330
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,964评论 3 315
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,772评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,004评论 1 266
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 46,401评论 2 360
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 43,566评论 2 349

推荐阅读更多精彩内容