我试图理解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()
答案 0 :(得分:4)
你正在使用一个邪恶的C风格的强制转换,在这种情况下等同于reinterpret_cast
,指针指向一个类并假装它指向一个不相关的类。
我原本期待一些编译错误/崩溃
没有编译错误,因为你故意阻止编译器检查类型转换 - 这是reinterpret_cast
的目的,以及避免它的原因(以及C风格的转换更是如此),除了什么时候真的有必要。可能存在崩溃或任何其他类型的未定义的运行时行为。
在您的情况下,即使指针类型完全错误,类布局似乎足以使虚函数调用成功。这并不奇怪,因为你的类定义都非常相似;但这是非常不保证的行为,原则上它可能会以许多灾难性的方式失败。
如果您需要多态行为,则Class2
和Class3
应该从Class1
继承。然后,指针转换将在没有强制转换的情况下生效,并且将很好地定义多态行为 - f()
将根据对象类型进行虚拟调度,而g()
将根据指针类型进行非虚拟调度。
答案 1 :(得分:3)
您的代码有未定义的行为。
当程序有未定义的行为时,一切都会发生。您可能会崩溃,但这不是必需的。从第1.3.24段开始:
[...]允许的未定义行为包括完全忽略无法预测的结果,在翻译或程序执行期间以环境特征(有或没有发出诊断消息)的文件化方式行事,终止翻译或执行(发布诊断消息)。 [...]
为什么会有未定义的行为?
您正在对指向reinterpret_cast<>
类型的对象的指针执行残酷的C样式转换(转换为Class3
)指向类型为Class1
的对象的指针。这是允许的,但行为未定义。
来自C ++ 11标准的第5.2.10 / 7段关于reinterpret_cast<>
:
可以将对象指针显式转换为不同类型的对象指针 .70 [..]
这使得显式演员合法化。但是(同一段):
[...]当“指向T1的指针”类型的prvalue v转换为该类型时 “指向cv T2的指针”,结果是static_cast(static_cast(v))如果T1和T2都是标准布局类型(3.9)并且 T2的对齐要求不比T1更严格,或者如果 两种类型都是无效的。 [...]
您的课程Class1
和Class3
不是标准布局类型。事实上,根据第9/7段:
标准布局类是一个类:
- 没有非标准布局类(或此类类型的数组)或引用类型的非静态数据成员,
- 没有虚拟功能(10.3)且没有虚拟基类(10.1),
[...]
因此,5.2.10 / 7的第二部分适用:
[...]将“指向T1的指针”类型的prvalue转换为“指向T2的指针”类型(其中T1和T2是对象类型,T2的对齐要求不比T1更严格)返回其原始类型产生原始指针 值。 任何其他此类指针转换的结果未指定。
由于未指定转换该指针的结果,因此尝试在其上调用函数会导致未定义的行为。
答案 2 :(得分:1)
您的代码不是多态性示例。您只是将对象指针互相投射。
你忘了扩展(继承)你的类,因为多态与继承有关。
例如试试这个:
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";
}
};