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

时间:2013-02-18 17:15:03

标签: c++ polymorphism

我试图理解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()

3 个答案:

答案 0 :(得分:4)

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

  

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

没有编译错误,因为你故意阻止编译器检查类型转换 - 这是reinterpret_cast的目的,以及避免它的原因(以及C风格的转换更是如此),除了什么时候真的有必要。可能存在崩溃或任何其他类型的未定义的运行时行为。

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

如果您需要多态行为,则Class2Class3应该从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更严格,或者如果   两种类型都是无效的。 [...]

您的课程Class1Class3 不是标准布局类型。事实上,根据第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";
    }
  };