简体   繁体   中英

Visual studio 2008 : Polymorphic behavior of C++

I am trying to understand Polymorphism behavior in C++ code. I got surprised by seeing the output of the the below program.

The output of the below code I was expecting some compilation error/crash at following programming statement. But output of this program quite surprised me.

p = (Class1*) &object3;
p->f();
p->g();

I couldn't understand why. I am using Visual studio 2008.

Code Snippet.

#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:

Function f() in Class1

Function g() in Class1

Function f() in Class2

Function g() in Class1

Function h() in Class3

Function g() in Class1

You are using an evil C-style cast, equivalent in this case to reinterpret_cast , to take a pointer to one class and pretend it's pointing to an unrelated class.

I was expecting some compilation error/crash

There is no compilation error, because you have deliberately prevented the compiler from checking the type conversion - that is the purpose of reinterpret_cast , and the reason to avoid it (and C-style casts even more so) except when it's really necessary. There could be a crash, or any other type of undefined runtime behaviour.

In your case, it appears that the class layouts are similar enough for the virtual function call to succeed even though the pointer type is completely wrong. This is not surprising, since your class definitions are all very similar; but this is very much not guaranteed behaviour, and in principle it could fail in many catastrophic ways.

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.

Your code has Undefined Behavior .

When a program has Undefined Behavior, everything could happen. You could get a crash, but this is not necessary. From Paragraph 1.3.24:

[...] Permissible undefined behavior ranges from ignoring the situation completely with unpredictable results, to behaving during translation or program execution in a documented manner characteristic of the environment (with or without the issuance of a diagnostic message), to terminating a translation or execution (with the issuance of a diagnostic message). [...]


Why do you have Undefined Behavior?

You are performing a brutal C-style cast (which resorts to a reinterpret_cast<> ) of a pointer to an object of type Class3 to a pointer to an object of type Class1 . This is allowed, but the behavior is undefined.

From Paragraph 5.2.10/7 of the C++11 Standard about reinterpret_cast<> :

An object pointer can be explicitly converted to an object pointer of a different type .70 [..]

This makes the explicit cast legal. However (same paragraph):

[...] When a prvalue v of type “pointer to T1” is converted to the type “pointer to cv T2”, the result is static_cast(static_cast(v)) if both T1 and T2 are standard-layout types (3.9) and the alignment requirements of T2 are no stricter than those of T1, or if either type is void. [...]

Your classes Class1 and Class3 are not standard-layout types. Per Paragraph 9/7, in fact:

A standard-layout class is a class that:

— has no non-static data members of type non-standard-layout class (or array of such types) or reference,

— has no virtual functions (10.3) and no virtual base classes (10.1),

[...]

Therefore, the second part of 5.2.10/7 applies:

[...] Converting a prvalue of type “pointer to T1” to the type “pointer to T2” (where T1 and T2 are object types and where the alignment requirements of T2 are no stricter than those of T1) and back to its original type yields the original pointer value. The result of any other such pointer conversion is unspecified .

Since the result of converting that pointer is unspecified, trying to invoke a function on it results in Undefined Behavior.

Your code is not a polymorphism sample. You are just casting object pointers to each other.

You forgotten to extend (inherit) your classes from each other, since polymorphism is related to inheritance.

For example try this:

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";
    }
  };

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM