2016-11-18 14 views
4

考慮這一段代碼:編譯器行爲構建虛基類時

#include <iostream> 

class A { 
    public: 
    A(int s) { std::cout << "A(" << s << ")\n"; } 
}; 

class B1 : virtual public A { 
    public: 
    B1(int s1, int s2) 
     : A{s1} { std::cout << "B1(" << s1 << "," << s2 << ")\n"; } 
}; 

class B2 : virtual public A { 
    public: 
    B2(int s1, int s2) 
     : A{s1} { std::cout << "B2(" << s1 << "," << s2 << ")\n"; } 
}; 

class C1 : public B1, public B2 { 
    public: 
    C1() : B1{1,2}, B2{3,4}, A{5} {} 
}; 

class C2 : public B1, public B2 { 
    public: 
    C2() : B1(1,2), B2(3,4), A{5} {} 
}; 

int main() 
{ 
    std::cout << "Create c1:\n"; 
    C1 c1; 

    std::cout << "\n"; 
    std::cout << "Create c2:\n"; 
    C2 c2; 

} 

類A是一個虛基類的B1和B2。類C1和C2是相同的,除了C1使用{...}和C2使用(...)來構造B1和B2。

由於此處使用了虛擬繼承,所以類A應構建爲C1或C2構造的一部分。

如果我編譯此代碼與微軟VS2015,它在運行時產生這樣的輸出:

Create c1: 
A(5) 
B1(1,2) 
B2(3,4) 

Create c2: 
A(5) 
B1(1,2) 
B2(3,4) 

這正是我所期望的。

但是,如果我與海灣合作委員會(6.1.0)編譯它,它會產生這樣的輸出:構建C2時

Create c1: 
A(5) 
A(1) 
B1(1,2) 
A(3) 
B2(3,4) 

Create c2: 
A(5) 
B1(1,2) 
B2(3,4) 

這裏,構建C1當A的構造函數被調用三次,但只有一次。

這是GCC中的錯誤還是我誤解了一些東西?

+0

您可能會考慮添加常規C++標記,以獲得更廣泛的受衆。 – Jonas

+0

好的,@Jonas,我用C++替換了C++ 11標籤。 (我不能超過5個標籤。) – oz1cz

+0

您是否也嘗試過Clang?我99%確定這是一個錯誤。 –

回答

1

回答我的問題:

顯然,GCC確實有在這種情況下的錯誤。使用GCC版本7.0.0編譯代碼會產生正確的輸出結果:

Create c1: 
A(5) 
B1(1,2) 
B2(3,4) 

Create c2: 
A(5) 
B1(1,2) 
B2(3,4) 
0

不太清楚爲什麼會這樣,但不應該一個類是多態的(至少有一個虛函數),以適當地從它繼承?

+0

試用了一個多態,用gcc 5.4.0,同樣的bug。 – RadioTransmission

+0

有趣的是,對於gcc,sizeof(c1)和sizeof(c2)是相同的,如果在A中聲明瞭方法,則可以從c1或c2中調用,而不會出現歧義。 – RadioTransmission

+0

虛擬基類通常是多態的,作爲抽象接口類。但是作爲基礎的屬性是派生類中的屬性;類型只是派生類中的虛擬或非虛擬基礎。 – curiousguy

0

bug是差多了:

#include <iostream> 

    class A { 
     public: 
      A(int s) { std::cout << "A(" << s << ")\n"; ms = s; }; 
      int ms; 
      virtual void dummy() {std::cout << "Aaaaaa!" << std::endl;}; 
      virtual ~A() {std::cout << "~A(" << ms << ")\n";}; 
    }; 

    class B1 : virtual public A { 
     public: 
      B1(int s1, int s2) 
       : A{s1} { std::cout << "B1(" << s1 << "," << s2 << ")\n"; }; 
    }; 

    class B2 : virtual public A { 
     public: 
      B2(int s1, int s2) 
       : A{s1} { std::cout << "B2(" << s1 << "," << s2 << ")\n"; }; 
    }; 

     class C1 : public B1, public B2 { 
      public: 
       C1() : B1{1,2}, B2{3,4}, A{5} {}; 
     }; 

     class C2 : public B1, public B2 { 
      public: 
       C2() : B1(1,2), B2(3,4), A{5} {}; 
     }; 

     int main() 
     { 
     { 
      std::cout << "Create c1:\n"; 
      C1 c1; 
      std::cout << "Calling A's dummy on c1: " << std::endl; 
      c1.dummy(); 
      std::cout << "Size of c1: " << sizeof(c1) << std::endl; 
     } 

     { 
      std::cout << "\n"; 
      std::cout << "Create c2:\n"; 
      C2 c2; 
      std::cout << "Calling A's dummy on c2: " << std::endl; 
      c2.dummy(); 
      std::cout << "Size of c2: " <<sizeof(c2) << std::endl; 
     } 

    } 

編譯上GCC 5.4.0 運行該代碼產生以下輸出:

  • 創建C1:
  • A(5)
  • A(1)
  • B1(1,2)
  • A(3)
  • B2(3,4)
  • 調用的假人在C1:
  • AAAAAA!
  • c1的尺寸:32
  • 〜A(3)

  • 創建C2:
  • A(5)
  • B1(1,2)
  • B2 (3,4)
  • 在c2上調用A的虛擬對象:
  • Aaa AAA!
  • C2的大小:32
  • 〜A(5)
+0

你在這裏發現了什麼新的錯誤?據我所知,除了原文中提到的錯誤之外,您的代碼的行爲完全如預期。 – oz1cz

+1

錯誤是一樣的,你是對的。只想指向A的析構函數調用。 – RadioTransmission