簡體   English   中英

使用虛擬繼承時初始化基類

[英]Initialising base classes when using virtual inheritance

在OS X上使用Xcode 5.1編譯以下代碼時,出現意外錯誤。Apple LLVM版本5.1(clang-503.0.40)(基於LLVM 3.4svn)

class GrandParent
{
public:
    GrandParent(int age) : m_age(age)
    {
    }

    virtual ~GrandParent() {}

private:
    GrandParent();
    GrandParent(const GrandParent&);
    const GrandParent& operator=(const GrandParent&);

    int m_age;
};

class Parent1 : public virtual GrandParent
{
public:
    Parent1(int age) : m_age(age)
    {
    }

    virtual ~Parent1() {}

private:
    Parent1();
    Parent1(const Parent1&);
    const Parent1& operator=(const Parent1&);

    int m_age;
};

class Parent2 : public virtual GrandParent
{
public:
    Parent2(int age) : m_age(age)
    {
    }

    virtual ~Parent2() {}

private:
    Parent2();
    Parent2(const Parent2&);
    const Parent2& operator=(const Parent2&);

    int m_age;
};

class Child : public Parent1, public Parent2
{
public:
    Child(int grandParentAge, int parent1Age, int parent2Age, int childAge) :
        GrandParent(grandParentAge),
        Parent1(parent1Age),
        Parent2(parent2Age),
        m_age(childAge)
    {
    }

    virtual ~Child() {}

private:
    Child();
    Child(const Child&);
    const Child& operator=(const Child&);

    int m_age;
};

報告的錯誤是:

error: inherited virtual base class 'GrandParent' has private default constructor
    Parent1(int age) : m_age(age)
    ^
note: declared private here
    GrandParent();
    ^
error: inherited virtual base class 'GrandParent' has private default constructor
    Parent2(int age) : m_age(age)
    ^
note: declared private here
    GrandParent();

我的理解是,從其繼承的類(Parent1或Parent2)不會調用虛擬基類(GrandParent)的構造函數。 而是由具體類(子級)的構造函數調用該構造函數。

這個對嗎?

如果我為GrandParent提供默認的構造函數,則可以編譯。 但是,如果我構造一個子對象:

Child child(80, 50, 49, 20);

並檢查它,我可以看到:

Child) child = {
  Parent1 = {
    GrandParent = (m_age = 49)
    m_age = 50
  }
  Parent2 = {
    GrandParent = (m_age = 80)
    m_age = 49
  }
  m_age = 20

因此,使用Parent1時GrandParent的年齡是不正確的,但對於Parent2來說是正確的。

我誤會了嗎? 還是該錯誤可能是編譯器錯誤?

UPDATE

如果我將Parent1的ctor更新(並且對Parent2執行相同的操作)為:

Parent1(年齡):GrandParent(100),m_age(年齡){}

現在可以編譯了。 檢查值顯示:

(Child) child = {
  Parent1 = {
    GrandParent = (m_age = 49)
    m_age = 50
  }
  Parent2 = {
    GrandParent = (m_age = 80)
    m_age = 49
  }
  m_age = 20

這顯然是不對的。 此外,修改后的代碼在Windows上使用VS 2013 Express編譯,並且檢查值正確。

程序執行時

 Parent1(int age) : m_age(age) 

它嘗試創建一個Parent1對象,該對象是GrandParent類型的派生類。 從Parent1類的構造函數定義可以看出,它什么也不做,只是通過調用默認的構造函數來嘗試創建GrandParent對象。

Parent1(int age) : m_age(age)   // Here the default constructor of GrandParent is called implicitly.
{
}

但是,由於您已將GranParent類的默認構造函數定義為私有,因此編譯器會給出該錯誤。

所有定義的ctor(默認值或未設置)都必須有效。

盡管在運行時 (除了最派生的ctor以外的所有對象)都會跳過虛擬基礎的初始化,但它仍然必須有效。

引用C ++ 14最終草案(n3936):

12.6.2初始化基礎和成員[class.base.init]

7 mem初始化器中expression-listbraced-init-list用於根據8.5的初始化規則對指定的子對象(或在委托構造函數的情況下,對完整的類對象)進行初始化。 。
[示例省略]
每個mem初始化程序執行的初始化構成一個完整表達式。 mem初始化程序中的任何表達式都將作為執行初始化的全表達式的一部分進行評估。 MEM-初始化其中MEM-初始化-ID表示虛基類中的任何類,它是不是最派生類的構造函數的執行期間被忽略。
8在非委托的構造函數中,如果給定的可能構造的子對象未由meminitializer-id指定(包括因為構造函數沒有ctor-initializer而沒有mem-initializer-list的情況),則

  • 如果實體是具有大括號或相等初始化程序的非靜態數據成員,並且
    • 構造函數的類是聯合(9.5),並且該聯合的其他任何變體成員都不由mem-initializer-id
    • 構造函數的類不是聯合體,並且,如果實體是匿名聯合體的成員,則該聯合體的其他任何成員都不由mem-initializer-id指定,該實體將按照8.5中的規定進行初始化;
  • 否則,如果實體是匿名聯合或變量成員(9.5),則不執行初始化;
  • 否則,該實體為默認初始化(8.5)

[注意:抽象類(10.4)絕不是派生程度最高的類,因此其構造函數從不初始化虛擬基類,因此可以省略相應的mem初始化器。 —尾注]

我特別建議您注意最后的注釋,您可能將其用作理由。
麻煩的是,注釋不是規范性的,並且此注釋與其前面的規范性文本完全矛盾。

似乎clang ++-3.5.0將此注釋當作福音,而g ++-4.9.0卻沒有:
http://coliru.stacked-crooked.com/a/ded8d46cc29ac79f

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM