Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Is it possible to downcast an object to a subclass which does not define extra variable or vtable in C++?

Tags:

c++

downcast

Is it possible to downcast an object to a subclass does not define any extra variable or virtual method?

If I have these classes,

class A { public: A (); };
class B : public A { public: void method1 () {} B (); };

is this (1) possible and (2) safe by standard?

A* a = new A ();
B* b = (B*)a;
b->method1();
like image 344
eonil Avatar asked Feb 19 '13 07:02

eonil


4 Answers

The pointer conversion acts as a static_cast. 5.2.9/2 says,

If the object of type [A] is actually a subobject of an object of type [B], the result refers to the enclosing object of type [B]. Otherwise, the result of the cast is undefined.

Your object is not a subobject of a B object, so the result is undefined.

Even if you were to reinterpret_cast, accessing the value of the object through the resulting pointer has undefined behavior because it violates strict aliasing. In the C++11 standard, 3.10/10:

If a program attempts to access the stored value of an object through a glvalue of other than one of the following types the behavior is undefined:

"A derived class of the dynamic type of the object that adds no data members or virtual member functions" is not in the list that follows.

Depending on what method1 actually does, it might be possible to avoid accessing the stored value of the object when calling it. But I'm not certain it's ever possible. Unless stated otherwise elsewhere in the standard I would assume for safety that calling a non-static member function inherently "accesses the stored value of the object" even if the function doesn't actually use any data members.

This is one of those awkward cases that will probably work in practice either all the time or almost all the time. But it's not guaranteed, so even if it appears to work and the emitted code looks OK, you will live in fear that some day a new optimization will break it.

Once defined, C++ classes are closed to new members, including new member functions. So your object created with new A() has all the member functions it will ever have. Just write a non-member function -- unless A has protected members it will have exactly the same access to A that your member function has. And if A does have protected members then there's an approved way of deriving from it, which you should use to create proper instances of B.

If member function syntax means that much to you, then depending on the class A you might be able to write:

B b = *a;    // "copy" the object (give B a suitable ctor)
b.method1(); // act on it
*a = b;      // copy it back (A needs copy assignment operator)

Obviously there are issues here that could stop it working: to begin with whether the object is copyable, also thread-safety, and whether method1 stores a pointer/reference to b somewhere that will start to dangle as soon as b is destroyed. In C++11 the copies could perhaps be moves for efficiency, but even so I hope you will agree that the hoops you have to jump through to use member function syntax are not worth it.

like image 86
Steve Jessop Avatar answered Sep 29 '22 13:09

Steve Jessop


  1. Yes, it is possible.
  2. No, it is not safe according to the standard; it is discouraged.

This is more like C void* casting, which is not the best thing to do in C++. But I did this many times and it works fine.

like image 39
Yousf Avatar answered Sep 29 '22 11:09

Yousf


B* b = static_cast< B* >a or what you tried is an unsafe downcasting,it assigns the address of a base-class object (A) to a derived class (B) pointer. So if you access anything throught that pointer, it will cause undefined behavior.

Let's assume one possible object layout of an instance of A:

a ->|vptr|

When you perform a forced cast:

b ->|vptr|

it is definitely unsafe since a doesn't point to an instance of B or subclass of B. When you call a virtual method or modify a field(not in this case), it will cause undefined behavior in general or error in this layout.

However, your method1 is non-virtual, so there is no need to look up virtual table. Since your implementation of the method1 doesn't and even can't do anything on "this", So when you run the code in this hypothetical object layout, it will probabaly report no error(See James' comment).

like image 44
StarPinkER Avatar answered Sep 29 '22 11:09

StarPinkER


You should read this extremely well-written post:

Regular cast vs. static_cast vs. dynamic_cast

If you use "static_cast", be warned that you are "force" converting and this is inherently unsafe.

like image 20
Rahul Banerjee Avatar answered Sep 29 '22 11:09

Rahul Banerjee