天天看點

C++11 FAQ中文版:繼承的構造函數

三 2 Year 2011 陳 良喬 C++11 FAQ

繼承的構造函數

人們有時會對類成員函數或成員變量的作用域問題感到困惑,尤其是,當基類與派生類的同名成員不在同一個作用域内時:

struct B {
        void f(double);
    };
    struct D : B {
        void f(int);
    };
    B b;   b.f(4.5);    // 可行
    // 調用的到底是B::f(doube)還是D::f(int)呢?
    // 實際情況往往會讓冉感到意外:調用的f(int)函數實參為4
    D d;   d.f(4.5);
           

在C++98标準裡,可以将普通的重載函數從基類“晉級”到派生類裡來解決這個問題:

struct B {
        void f(double);
    };

    struct D : B {
        using B::f;     // 将類B中的f()函數引入到類D的作用域内
        void f(int);    // 增加一個新的f()函數
    };

    B b;   b.f(4.5);    // 可行
    // 可行:調用類D中的f(double)函數
    // 也即類B中的f(double)函數
    D d;   d.f(4.5);
           

普通重載函數可以通過這種方式解決,那麼,對于構造函數又該怎麼辦呢? 我已經說過“不能像應用于普通成員函數那樣,将上述文法應用于構造函數,這如曆史偶然一樣”。為了解決構造函數的“晉級”問題,C++11提供了這種能力:

class Derived : public Base {
    public:
        // 提升Base類的f函數到Derived類的作用範圍内
        // 這一特性已存在于C++98标準内
        using Base::f;
        void f(char);     // 提供一個新的f函數
        void f(int);  // 與Base類的f(int)函數相比更常用到這個f函數

        // 提升Base類的構造函數到Derived的作用範圍内
        // 這一特性隻存在于C++11标準内
        using Base::Base;
        Derived(char);    // 提供一個新的構造函數
        // 與Base類的構造函數Base(int)相比
        // 更常用到這個構造函數
        Derived(int);
        // …
    };
           

如果這樣用了,仍然可能困惑于派生類中繼承的構造函數,這個派生類中定義的新成員變量需要初始化(譯注:基類并不知道派生類的新增成員變量,當然不會對其進行初始化。):

struct B1 {
        B1(int) { }
    };
    struct D1 : B1 {
        using B1::B1; // 隐式聲明構造函數D1(int)
        int x;
    };
    void test()
    {
        D1 d(6);  // 糟糕:調用的是基類的構造函數,d.x沒有初始化
        D1 e;    // 錯誤:類D1沒有預設的構造函數
}
           

我們可以通過使用成員初始化(member-initializer)消除以上的困惑:

struct D1 : B1 {
            using B1::B1;    // 隐式聲明構造函數D1(int)
            // 注意:x變量已經被初始化
            // (譯注:在聲明的時候就提供初始化)
            int x{0};
        };
        void test()
        {
            D1 d(6);    // d.x的值是0
        }
           

或參見:

  • the C++ draft 8.5.4 List-initialization [dcl.init.list]
  • [N1890=05-0150 ] Bjarne Stroustrup and Gabriel Dos Reis:

    Initialization and initializers

    (an overview of initialization-related problems with suggested solutions).

  • [N1919=05-0179] Bjarne Stroustrup and Gabriel Dos Reis:

    Initializer lists.

  • [N2215=07-0075] Bjarne Stroustrup and Gabriel Dos Reis :

    Initializer lists (Rev. 3) .

  • [N2640=08-0150] Jason Merrill and Daveed Vandevoorde:

    Initializer Lists — Alternative Mechanism and Rationale (v. 2) (final proposal).