Visual studio 2008:C++的多态行为

Visual studio 2008 : Polymorphic behavior of C++

本文关键字:多态 C++ studio 2008 Visual      更新时间:2023-10-16

我正在努力理解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 Class1n";
      }
     void g()
     {
    cout << "Function g() in Class1n";
     }
 };

 class Class2
 {
    public:
   virtual void f()
   {
    cout << "Function f() in Class2n";
   }
   void g()
   {
    cout << "Function g() in Class2n";
   }
 };

class Class3
{
    public:
    virtual void h()
    {
    cout << "Function h() in Class3n";
    }
  };

  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样式强制转换更是如此),除非真的有必要。可能存在崩溃,或者任何其他类型的未定义运行时行为。

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

如果您想要多态行为,那么Class2Class3应该继承自Class1。指针转换将在没有强制转换的情况下有效,多态行为将得到很好的定义——f()将根据对象类型进行虚拟调度,g()将根据指针类型进行非虚拟调度。

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

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

[…]允许的未定义行为范围从完全忽略情况并产生不可预测的结果,到在翻译或程序执行过程中以环境特有的记录方式行事(有或没有发布诊断消息),再到终止翻译或执行(有诊断消息发布)。[…]


为什么你有未定义的行为

您正在执行一个残酷的C样式转换(使用reinterpret_cast<>),将指向Class3类型对象的指针转换为指向Class1类型对象的指示器。这是允许的,但行为是未定义的。

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

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

这使得明确的演员阵容合法。但是(同一段):

[…]当类型为"pointer to T1"的prvalue v转换为类型"指针指向cv T2",如果T1和T2都是标准布局类型(3.9)并且T2的对准要求并不比T1的对准要求更严格,或者如果任何一种类型都是无效的。[…]

Class1Class3而不是标准布局类型。根据第9/7段,事实上:

标准布局类是指:

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

--没有虚拟函数(10.3),也没有虚拟基类(10.1),

[…]

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

[…]将类型为"pointer to T1"的prvalue转换为类型为"pointer to T2"(其中T1和T2是对象类型,T2的对齐要求不比T1严格)并返回到其原始类型会产生原始指针值任何其他此类指针转换的结果都是未指定的

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

您的代码不是多态性样本。您只是将对象指针投射到彼此之间。

您忘记了相互扩展(继承)您的类,因为多态性与继承有关。

例如,试试这个:

class Class1
{
   public:
      virtual void f()
      {
    cout << "Function f() in Class1n";
      }
     void g()
     {
    cout << "Function g() in Class1n";
     }
 };

 class Class2 : public Class1
 {
    public:
   virtual void f()
   {
    cout << "Function f() in Class2n";
   }
   void g()
   {
    cout << "Function g() in Class2n";
   }
 };

class Class3 : public Class2
{
    public:
    virtual void h()
    {
    cout << "Function h() in Class3n";
    }
  };