[英]Visual studio 2008 : Polymorphic behavior of C++
我試圖了解C ++代碼中的多態行為。 看到下面程序的輸出,我感到很驚訝。
以下代碼的輸出我期望在以下編程語句時出現一些編譯錯誤/崩潰。 但是這個程序的輸出讓我很驚訝。
p = (Class1*) &object3;
p->f();
p->g();
我不明白為什么。 我正在使用Visual Studio 2008。
代碼段。
#include "stdafx.h"
#include <iostream>
using namespace std;
class Class1
{
public:
virtual void f()
{
cout << "Function f() in Class1\n";
}
void g()
{
cout << "Function g() in Class1\n";
}
};
class Class2
{
public:
virtual void f()
{
cout << "Function f() in Class2\n";
}
void g()
{
cout << "Function g() in Class2\n";
}
};
class Class3
{
public:
virtual void h()
{
cout << "Function h() in Class3\n";
}
};
int _tmain(int argc, _TCHAR* argv[])
{
Class1 object1, *p;
Class2 object2;
Class3 object3;
p = &object1;
p->f();
p->g();
p = (Class1*) &object2;
p->f();
p->g();
p = (Class1*) &object3;
p->f();
p->g();
//p->h(); Compilation error
return 0;
}
O / P:
Class1中的函數f()
Class1中的函數g()
Class2中的函數f()
Class1中的函數g()
Class3中的函數h()
Class1中的函數g()
您使用的是邪惡的C樣式強制轉換,在這種情況下等效於reinterpret_cast
,以獲取指向一個類的指針,並假裝它指向無關的類。
我期待一些編譯錯誤/崩潰
沒有編譯錯誤,因為您故意阻止了編譯器檢查類型轉換-這是reinterpret_cast
的目的,以及避免這樣做的原因(以及C樣式強制轉換更是如此),除非確實需要。 可能發生崩潰,或任何其他類型的未定義運行時行為。
在您的情況下,即使指針類型完全錯誤,類布局也足夠使虛擬函數調用成功。 這並不奇怪,因為您的類定義都非常相似。 但這是無法保證的行為,原則上它可能會以許多災難性的方式失敗。
如果您想要多態行為,那么Class2
和Class3
應該從Class1
繼承。 這樣,指針轉換將有效且無強制轉換,並且多態行為將得到很好的定義f()
將根據對象類型虛擬地分派,而g()
根據指針類型虛擬地分派。
您的代碼具有未定義的行為 。
當程序具有未定義行為時,一切都會發生。 您可能會崩潰,但這不是必需的。 從第1.3.24段開始:
[...]允許的不確定行為范圍從完全忽略具有不可預知結果的情況到在翻譯或程序執行過程中以環境特征的書面方式記錄的行為(有無診斷消息)到終止翻譯或執行(發出診斷消息)。 [...]
為什么會有未定義的行為?
你是(到其求助於進行野蠻C樣式轉換reinterpret_cast<>
的指針,以類型的對象Class3
的指針類型的對象Class1
。 這是允許的,但是行為是不確定的。
根據C ++ 11標准的5.2.10 / 7段中有關reinterpret_cast<>
:
對象指針可以顯式轉換為其他類型的 .70 [..] 對象指針
這使得顯式強制轉換合法。 但是(同一段):
[...]將類型為“指向T1的指針”的prvalue v轉換為類型為“指向cv T2的指針”時, 如果T1和T2均為標准布局類型 (3.9,則結果為static_cast(static_cast(v))) ),並且T2的對齊要求不比T1嚴格,或者兩種類型均無效。 [...]
您的Class1
和Class3
類不是標准布局類型。 實際上,根據第9/7段:
標准布局類是這樣的類:
—沒有非標准布局類(或此類數組)或引用的非靜態數據成員,
— 沒有虛擬函數 (10.3)和虛擬基類(10.1),
[...]
因此,5.2.10 / 7的第二部分適用:
[...]將“指向T1的指針”的prvalue轉換為“指向T2的指針”的類型(其中T1和T2是對象類型,並且T2的對齊要求不嚴格於T1的對齊要求)並返回到其原始類型產生原始指針值。 未指定任何其他此類指針轉換的結果 。
由於未指定轉換該指針的結果,因此嘗試在其上調用函數會導致未定義行為。
您的代碼不是多態示例。 您只是在互相投射對象指針。
您忘記了彼此之間擴展(繼承)您的類,因為多態與繼承有關。
例如,嘗試以下操作:
class Class1
{
public:
virtual void f()
{
cout << "Function f() in Class1\n";
}
void g()
{
cout << "Function g() in Class1\n";
}
};
class Class2 : public Class1
{
public:
virtual void f()
{
cout << "Function f() in Class2\n";
}
void g()
{
cout << "Function g() in Class2\n";
}
};
class Class3 : public Class2
{
public:
virtual void h()
{
cout << "Function h() in Class3\n";
}
};
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.