0

我试图了解 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;

   }

输出/输出:

Class1 中的函数 f()

Class1 中的函数 g()

Class2 中的函数 f()

Class1 中的函数 g()

Class3 中的函数 h()

Class1 中的函数 g()

4

3 回答 3

4

您正在使用邪恶的 C 风格强制转换,在这种情况下等同于reinterpret_cast,来获取指向一个类的指针并假装它指向一个不相关的类。

我期待一些编译错误/崩溃

没有编译错误,因为您故意阻止编译器检查类型转换——这就是reinterpret_cast. 可能会发生崩溃或任何其他类型的未定义运行时行为。

在您的情况下,即使指针类型完全错误,类布局似乎也足以使虚函数调用成功。这并不奇怪,因为您的类定义都非常相似;但这在很大程度上是不能保证的行为,原则上它可能会以许多灾难性的方式失败。

If you want polymorphic behaviour, then Class2 and Class3 should inherit from Class1. The pointer conversions will then be valid with no cast, and the polymorphic behaviour will be well defined - f() will be dispatched virtually according to the object type, and g() non-virtually according to the pointer type.

于 2013-02-18T17:22:46.473 回答
3

您的代码具有未定义的行为

当程序具有未定义的行为时,一切都可能发生。您可能会发生崩溃,但这不是必需的。从第 1.3.24 段:

[...] 允许的未定义行为的范围从完全忽略具有不可预测结果的情况,到在翻译或程序执行期间以环境特征的记录方式表现(有或没有发出诊断消息),到终止翻译或执行(发出诊断消息)。[...]


为什么你有未定义的行为?

您正在执行一个残酷的 C 风格强制转换(诉诸 a reinterpret_cast<>),将指向类型对象的Class3指针转换为指向类型对象的指针Class1。这是允许的,但行为未定义。

从 C++11 标准的第 5.2.10/7 段关于reinterpret_cast<>

对象指针可以显式转换为不同类型的对象指针.70 [..]

这使得显式强制转换是合法的。但是(同一段):

[...] 当类型为“指向 T1 的指针”的纯右值 v 转换为类型“指向 cv T2”的指针时,如果 T1 和 T2 都是标准布局类型(3.9 ) 并且 T2 的对齐要求不比 T1 更严格,或者如果任一类型为 void。[...]

您的类不是Class1标准Class3布局类型。根据第 9/7 段,事实上:

标准布局类是这样的类:

— 没有非标准布局类(或此类类型的数组)或引用类型的非静态数据成员,

没有虚函数(10.3) 和虚基类 (10.1),

[...]

因此,5.2.10/7 的第二部分适用:

[...] 将“指向 T1 的指针”类型的纯右值转换为“指向 T2 的指针”类型(其中 T1 和 T2 是对象类型,并且 T2 的对齐要求不比 T1 的对齐要求更严格)并返回到它的原始类型产生原始指针值。任何其他此类指针转换的结果都是未指定的

由于未指定转换该指针的结果,因此尝试对其调用函数会导致未定义行为。

于 2013-02-18T17:19:40.237 回答
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";
    }
  };
于 2013-02-18T17:18:28.447 回答