Is the "this" pointer RTTI-enabled?
Asked Answered
D

3

5

I'm trying to discover the most-derived class of an object, within a constructor of one of the classes in its inheritance tree. I've spent several hours on this now and am at a loss for how else I can do it or why it doesn't make sense. It seems to make perfect sense, yet it refuses to work. I've found numerous pages about RTTI and gotten basically nowhere with them. I'll continue explaining after my test case and its output.

The source:

#include <iostream>
#include <typeinfo>
#include <string>

class A
{
public:
  A(std::string foo);

  virtual void bar(A* a) = 0;
};

class B : public A
{
public:
  B();

  virtual void bar(A* a);
};

A::A(std::string foo)
{
  std::cout << "type as passed to A constructor: " << foo << " (" << this << ")" << std::endl;
  std::cout << "type as determined in A constructor: " << typeid(*this).name() << " (" << this << ")" << std::endl;
}

B::B() : A(typeid(*this).name())
{
  A* a = (A*)this;
  std::cout << "type as determined in B constructor: " << typeid(*a).name() << " (" << this << ")" << std::endl;
  this->bar(this);
}

void B::bar(A* a)
{
  std::cout << "type as determined in bar: " << typeid(*a).name() << " (" << a << ")" << std::endl;
}

int main()
{
  B b;
  b.bar(&b);

  return 0;
}

The output (on g++):

type as passed to A constructor: 1B (0x7fff5fbff910)
type as determined in A constructor: 1A (0x7fff5fbff910)
type as determined in B constructor: 1B (0x7fff5fbff910)
type as determined in bar: 1B (0x7fff5fbff910)
type as determined in bar: 1B (0x7fff5fbff910)

I'm trying to get the second line of the output to say "1B" instead of "1A". Is the RTTI stripped from "this" for some reason I can't yet imagine? How does that not break the idea of virtual functions? (I had implemented this with virtual functions until I discovered I was reimplementing part of RTTI, which I hadn't known of before.) As the output shows, I can make this work if I avoid using "this," but the need to do that would seem like breakage by design.

Denominational answered 20/6, 2012 at 5:46 Comment(2)
why are you trying to detect the type? that's ungood. this sounds like an attempted (flawed) solution to something else, what is that something else?Fluky
Alf, I have std::maps of types (std::strings, in this case) to objects, with each type having a unique object within the map. Each instance of a certain class has its own such map, for allowing new objects to be easily added to these core objects and later retrieved by type. (I was doing this with const ints and a web of virtual methods before I knew of RTTI, with equivalent failure for the same reason.)Denominational
M
4

You cannot do that because you misunderstand the dynamics involved.

The rule is:

The this in constructor/destructor of any class points to the class whose constructor/destructor it is.

Also, this is the reason virtual functions called in constructor do not behave as you would normally expect a virtual function to work when using dynamic dispatch.

You should detect the type, after the constructor has been called.You can do so in any of the member functions except the constructor and the destructor.

Micheal answered 20/6, 2012 at 5:49 Comment(1)
Alright, but why is that considered better? It seems unintuitive at best, to me. Would it break some other OO feature?Denominational
C
3

You are seeing expected behaviour. In the body of a constructor the type of the object under construction is the same as the type of the class whose constructor is being executed and not the class of the most derived class being constructed.

The anomaly is really in the expression typeid(*this) when used in a member initializer expression. This used to be undefined behavior in C++03 but this has been changed in C++11 so that you are getting the type of the constructor's class rather than the type of the actual object which is yet to be constructed.

Certiorari answered 20/6, 2012 at 5:55 Comment(0)
M
0

Consider your inheritance relation, you should already know that the constructor of A will execute first and then the one of B will. Within the constructor of A, the object is not yet a B as it has not been constructed. Hence, the dynamic type of an object within a constructor is always the type of the class itself and never the type of a derived class. The same happens in destructors.

In short words, what you want to do cannot be done at construction time.

Mobocracy answered 20/6, 2012 at 5:49 Comment(1)
"the constructor of A will execute first and then the one of B will." That's what I thought until I was able to pass "this" as a B into the A constructor (which is demonstrated implicitly in the test case). I now understand the flow as the most-derived constructor executing first, and each constructor calling its parent before doing anything else. Thus the bodies run from the root down as you say.Denominational

© 2022 - 2024 — McMap. All rights reserved.