【问题标题】:Call to virtual function steps into wrong function when called from constructor of derived class, but not when from parent class从派生类的构造函数调用时,调用虚函数会进入错误的函数,但从父类调用时不会
【发布时间】:2011-11-01 11:53:40
【问题描述】:

我有下面的类结构(细节省略)。

Environment.h

#include "SimEnv.h"

class Environment : public SimEnv {
public:
  virtual int functionA(int arg) {
    printf("I'm in functionA\n");
    . . .
  }
  virtual int functionB(int arg) {
    printf("I'm in functionB\n");
    . . .
  }
};

DerivedEnv.h

#include "Environment.h"

class DerivedEnv : public Environment {
public:
  DerivedEnv(GrandParentClass* grandpaClass) : Environment() { . . . }
};

ParentClass.h

#include "GrandParentClass.h"
#include "Environment.h"

class ParentClass : public GrandParentClass {
public:
  ParentClass() {
    printf("In ParentClass constructor\n");
    m_pEnv = new DerivedEnv(this);
    m_pEnv->functionB(val);
  }
protected:
  Environment* m_pEnv;

  virtual void thisFunct() {
    printf("Calling functionB from ParentClass::thisFunct()\n");
    m_pEnv->functionB(val);
  }

};

DerivedClass.h

#include "ParentClass.h"

class DerivedClass : public ParentClass {
public:
  DerivedClass() : ParentClass() {
    printf("In DerivedClass constructor\n");
    m_pEnv->functionB(val);
  }
  void thatFunct {
    printf("Calling functionB from DerivedClass::thatFunct()\n");
    m_pEnv->functionB(val);
  }
  void sim() {
    thisFunct();
    printf("Calling functionB from DerivedClass::sim()\n");
    m_pEnv->functionB(val);
  }

};

ma​​in.cc

#include "DerivedClass.h"
void main() {
  . . .
  static DerivedClass derivedClass;
  derivedClass.sim();
  . . .
}

我遇到的问题是,当我运行我的代码并实例化 DerivedClass 时,我得到以下信息:

In ParentClass constructor
I'm in functionB
In DerivedClass constructor
I'm in functionA
Calling functionB from ParentClass::thisFunct()
I'm in functionB
Calling functionB from DerivedClass::sim()
I'm in functionA

所以我的程序在 ParentClass 的构造函数中调用了正确的虚函数,但随后在 DerivedClass 的构造函数中调用了错误的函数。这是我到目前为止检查过的内容:

  1. 我已经确定这不是我的构建被破坏的问题。
  2. 我已经在ParentClass 的构造函数和DerivedClass 的构造函数中使用p /a (*(void ***)m_pEnv)[0]@30 打印出gdb 中的vtable。两个 vtable 显示相同

我在互联网上搜索过,似乎这些类型的问题可能是由于堆栈损坏造成的。真的吗?我该如何调试它?这个问题还有其他解释吗?

加法 我进一步调试并意识到这里似乎存在上下文问题。当我从任何 ParentClass 的成员函数调用 functionB 时,它确实正确执行它。

似乎只是从 DerivedClass 调用到了错误的函数。

我已修改伪代码以突出显示这一点。

【问题讨论】:

  • 能否请您不要省略重要的细节,例如m_pEnv?实际上,废弃它——请发布真实的、可编译的代码,而不是伪代码。不然麻烦太多了。制作一个最小的、完整的示例来展示您的问题。
  • 修复了这个伪代码的所有问题后,我的版本在两行都打印了I'm in functionB。我的投票是“它在其他地方坏了”。
  • 伪代码没有显示任何类曾经引用functionA,请在真实代码中用functionA解释完整模式。
  • 感谢您的 cmets。不幸的是,发布真正的代码是不切实际的。它实际上是跨越数百个文件的多线程 Verilog/C++/SystemC 协同仿真环境的一部分。所以很可能是“它在其他地方坏了”。我知道在没有所有信息的情况下很难给出建议,我并不期待完整的解决方案,我会很高兴有关于如何进行调试的建议或想法。
  • 我唯一一次看到编译器从一个函数转到另一个函数(不是不同的虚拟重载)是在头文件中添加虚拟函数后没有重新编译某些东西。我会首先仔细检查构建,即使这意味着删除所有库/对象并从头开始构建。

标签: c++ constructor gdb virtual-functions


【解决方案1】:

您不应该从构造函数或析构函数调用虚函数。原因是派生类的对象此时不完整。

【讨论】:

  • 此处不适用。他们没有被this 调用。
【解决方案2】:

我发现出了什么问题。它实际上很平淡无奇。这是在不同位置的具有相同文件名的文件中定义同一类的两个不同版本的情况。

我认为这是一个预处理器问题。我不完全理解,但这是我的初步解释。

我已经编辑了上面的伪代码。我的目录结构是这样的:

include/Environment.h
include/DerivedEnv.h
include/Environment.h
include/SimEnv.h
include/ParentClass.h
include/GrandParentClass.h
sim/main.cc
sim/DerivedClass.h
...
distant_remote_directory/SimEnv.h -> this file slightly different from include/SimEnv.h

因为distant_remote_directory 在提供给g++ 的包含路径中,所以main.h 从那里获取它。但是在编译 ParentClass 时,由于文件 SimEnv.h 位于同一目录中,它会在远距离远程目录中的文件之前被拾取。所以 DerivedClass 和 ParentClass 看到的是同一类的不同版本。

感谢大家的帮助。

【讨论】:

    猜你喜欢
    • 2011-09-27
    • 2019-04-19
    • 1970-01-01
    • 2011-05-03
    • 2020-09-29
    • 2016-07-19
    • 2012-11-06
    • 2018-07-21
    • 2020-02-25
    相关资源
    最近更新 更多