“this”指针是否启用了RTTI?

5
我正在尝试在继承树中的一个类的构造函数中,发现对象的最派生类。我已经花了几个小时来解决这个问题,但是我无法弄清楚该怎么做或者为什么它不起作用。它似乎是完全合理的,但是它拒绝工作。我已经找到了很多关于RTTI的页面,但基本上没有得到任何有用的信息。在我的测试案例及其输出之后,我将继续解释。
#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;
}

输出结果(在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)

我正在尝试将输出的第二行从“1A”更改为“1B”。我不能想象为什么“this”上的RTTI会被剥离?这不会破坏虚函数的概念吗?(在我发现我正在重新实现RTTI的一部分之前,我已经使用虚函数实现了这个功能,但我以前不知道)。正如输出所示,如果我避免使用“this”,那么我可以使其工作,但是需要这样做似乎就像是设计上的问题。

2
为什么你要尝试检测类型?这不好。这听起来像是对其他问题的一种(有缺陷的)尝试解决,那个问题是什么? - Cheers and hth. - Alf
Alf,我有一些类型(在这种情况下是std::strings)到对象的std::maps,每种类型在地图中都有一个唯一的对象。某个类的每个实例都有自己的这样的映射,以便可以轻松地向这些核心对象添加新对象,并稍后按类型检索它们。(在了解RTTI之前,我使用const ints和虚拟方法网络来完成此操作,由于相同原因而失败。) - Grault
3个回答

4
您不能这样做,因为您误解了其中涉及的动态性。
规则是:
“任何类的构造函数/析构函数中的this指向其自身。”
这也是在构造函数中调用虚函数时,使用动态分派时它不能像通常期望的那样工作的原因。
您应该在构造函数和析构函数之外的任何成员函数中检测类型,但必须在构造函数调用之后执行。

好的,但为什么这被认为是更好的呢?对我来说,这似乎最多是不直观的。它会破坏其他面向对象编程特性吗? - Grault

3
您看到的是预期行为。在构造函数的主体中,正在构建的对象的类型与执行构造函数的类的类型相同,而不是正在构建的最终派生类的类。
异常实际上出现在成员初始化表达式中使用 typeid(*this) 的表达式中。在C++03中,这曾经是未定义行为,但在C++11中已经更改,因此您正在获取构造函数类的类型,而不是尚未构建的实际对象的类型。

0
考虑到继承关系,您应该已经知道 A 的构造函数会先执行,然后才是 B 的构造函数。在 A 的构造函数内部,对象还不是一个 B,因为它还没有被构造。因此,在构造函数中一个对象的动态类型始终是类本身的类型,而不是派生类的类型。析构函数也是如此。

简而言之,在构造时你所需做的无法完成。


“首先执行A的构造函数,然后才是B的构造函数。” 这就是我之前的想法,直到我能够将“this”作为B传递到A构造函数中(这在测试用例中隐式地演示)。 我现在理解的流程是:最终派生类的构造函数首先执行,每个构造函数在执行任何其他操作之前都会调用其父级。 因此,正如您所说,函数体从根部向下运行。 - Grault

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接