簡體   English   中英

這是VC ++ 2010中的錯誤嗎?

[英]Is this a bug in VC++ 2010?

當我在C ++中嘗試鑽石問題時遇到了這個問題。 以下程序對我而言在Visual Studio 2010中有效,並且得到以下輸出。

D
D
A
A
B
Error7
C
C
5
7
7
Press any key to continue . . .

如果您有任何解釋,請向我解釋。

注意,任何B對象都絕不會以任何方式構造。 大多數派生類D僅從C和A派生。 不是B。我想知道m_i2從哪里獲得值7?

#include "stdafx.h"
#include <iostream>
using namespace std;
class A{ int m_i1;
public:
    A(int i1){ m_i1 = i1;}  
    int GetMI1(){ return m_i1;} 
    void printABCD(){cout << "A" << endl;}
};
class B : public   A {
    int m_i2;
public:
    B(int i1):A(i1){} 
    void printABCD(){cout << "B" << endl;}
    void printEFGH(){cout << "Error" << m_i2 << endl;}
};

class C : virtual public A{
public:
    C(int i1):A(i1){}
    void printABCD(){cout << "C" << endl;}

};

class D : public C, public virtual A{
public:
    D(int i1):C(i1+1),A(i1){}
    void printABCD(){cout << "D" << endl;}
};

int _tmain(int argc, _TCHAR* argv[])
{
    A a(5); D d(7); C c(7); D* e = new D(7);
    d.printABCD();
    e->printABCD();
    ((A)d).printABCD();
    ((A*)e)->printABCD();
    ((B*)e)->printABCD();
    ((B*)e)->printEFGH();  // This line works perfectly, but why??
    //((B)(d)).printABCD(); //This will error and its right
    ((C*)e)->printABCD();
    ((C)d).printABCD();
    cout << a.GetMI1() << endl;
    cout << c.GetMI1() << endl;
    cout << d.GetMI1() << endl;
    system("pause");
    return 0;
}

Undefined behaviour is undefined. 解除引用(B*)e的那一刻,您的程序就具有未定義的行為(因為e實際上並未指向B對象),因此任何事情都可能發生。 該程序似乎可以運行,可能會崩潰,可以在線訂購披薩。

對該行為的一種可能解釋可能是D的布局恰好使得C子對象位於內存中的首位,並且具有與int相同的大小(它可能僅包含指向虛擬基數A的指針),並且其后是A子對象的成員m_i1D對象起點的偏移量與成員m_i2B對象起點的偏移量相同。 因此,代碼在執行B::printEFGH()A::m_i1解釋為B::m_i2 但這純粹是猜測。 與任何UB一樣,幾乎任何事情都可能發生。

編譯器正在生成的代碼完全可以執行您所要執行的操作(並且您正在“告訴”它以調用未定義的行為)。 該函數調用你是不是虛擬的,因此有編譯器可以(而且會)生成代碼,只需按下this到堆棧並調用一個成員函數,字面上的call ,而不需要vtable的分辨率。

這是完全不確定的行為 ,我希望這很清楚,所以不要這樣做。 您獲得的值是發生在與要傳遞的對象的基址偏移的內存中的任何值( 不是 B派生)。

如果您想看到一些奇怪的東西(同樣是UB)。 class D添加一個int成員m_dval ,然后在D結構上將其設置為42,然后UB是否完全按照您的要求進行調用。 我認為這可能會讓您感到驚訝,因為m_i2成員值(不是,btw)被報告m_i2

首先,您的代碼包含行為未定義的無效調用。

D類的祖先中沒有B類。 這意味着從D *B *任何強制轉換以及隨后使用結果指針指向B類型對象的任何嘗試均已無效。 如果您使用C ++樣式的static_caststatic_cast ),則編譯器會告訴您有關它的信息(就像它對(B)(d)嘗試所做的那樣)。 嘗試分析此類轉換后執行的任何調用沒有任何意義。 即這個

((B*)e)->printABCD();
((B*)e)->printEFGH();

具有未定義的行為。

其次,您說您正在“用C ++進行鑽石問題的實驗”。 您的代碼中與“鑽石問題”有關的唯一部分實際上是此調用

cout << d.GetMI1() << endl;

它顯示了D(7) -> A(7)初始值設定項勝過D(7) -> C(8) -> A(8)初始值設定項。 其余的調用與任何“鑽石”效果完全無關。

暫無
暫無

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

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