c++ - 同名类之间的共享 vtables : call to virtual method crashes when casting to base type

标签 c++

检查下面的更新,我可以重现并需要帮助。

我有一个奇怪的崩溃,其中一些方法在除 1 个地方之外的任何地方都可以正常工作。这是代码:

struct base
{
    virtual wchar_t* get() = 0; // can be { return NULL; } doesn't matter
};

struct derived: public base
{
    virtual wchar_t* get() { return SomeData(); }
};

struct container 
{
    derived data;
};

// this is approx. how it is used in real program
void output(const base& data) 
{ 
     data.get(); 
}

smart_ptr<container> item = GetItSomehow();
derived &v1 = item->data;
v1.get(); // works OK
//base &v2 = (base&)derived; // the old line, to understand old comments in the question
base &v2 = v1; // or base* v2 doesn't matter
v2.get(); // segmentation fault without going into method at all

现在,正如我所说,我在不同对象的许多地方调用 item->data.get() 并且它工作......总是。除了1个地方。但它在转换为基类时不起作用(输出 是需要它的一个例子)。

现在的问题是 - 如何为什么这会发生?我怀疑是纯虚拟调用,但我没有在构造函数中调用虚拟方法。我看不出这些电话有何不同。我怀疑基方法是抽象的,但如果我向它添加一个主体,它是相同的。

我不能提供一个小例子来测试,因为正如我所说,它总是有效,除了 1 个地方。如果我知道为什么它在那里不起作用,我就不需要测试样本了,因为那已经是答案了……

附言环境是 Ubuntu 11.10 x64,但程序是使用 gcc 4.5.2 自定义构建为 32 位编译的。

附言另一个线索,不确定是否相关...

warning: can't find linker symbol for virtual table for `derived::get' value
warning:   found `SomeOtherDerivedFromBaseClass::SomeOtherCrazyFunction' instead

在真实的程序中

更新:由于 gcc 将 vtable 链接到具有相同名称但位于不同共享库中的错误类,是否有可能发生这种情况?实际应用程序中的“派生”类实际上定义在多个共享库中,更糟糕的是,还有另一个具有相同名称但不同接口(interface)的类似类。奇怪的是,如果不强制转换为基类,它就可以工作。

我对这里的 gcc/linking/vtables 细节特别感兴趣。

这是我似乎重现的方式:

// --------- mod1.h
class base
{
public:
   virtual void test(int i); // add method to make vtables different with mod2
   virtual const char* data();
};

class test: public base
{
public:
   virtual const char* data();
};


// --------- mod2.h
class base
{
public:
   virtual const char* data();
};

class test: public base
{
public:
   virtual const char* data();
};

// --------- mod2.cpp
#include "mod2.h"
const char* base::data() { return "base2"; }
const char* test::data() { return "test2"; }

// --------- modtest.cpp
#include <stdio.h>
// !!!!!!!!! notice that we include mod1
#include "mod1.h"

int main()
{
   test t;
   base& b = t;
   printf("%s\n", t.data());
   printf("%s\n", b.data());
   return 0;
}

// --------- how to compile and run
g++ -c mod2.cpp && g++ mod2.o modtest.cpp  && ./a.out

// --------- output from the program
queen3@pro-home:~$ ./a.out 
test2
Segmentation fault

在上面的 modtest 中,如果我们包含“mod2.h”而不是“mod1.h”,我们会得到没有段错误的正常“test2\ntest2”输出。

问题是——具体的机制是什么?如何发现和预防?我知道 gcc 中的静态数据将链接到单个内存条目,但是 vtables...

最佳答案

编辑响应更新: 在您使用 mod1mod2 header 的更新代码中,您违反了类的单一定义规则(即使出现在共享库中)。它基本上指出,在您的整个程序中,您必须只有一个类定义(在本例中为 base),尽管相同的定义可以出现在多个源文件中。如果您有多个定义,那么所有赌注都将取消并且您会得到未定义的行为。在这种情况下,未定义的行为恰好是崩溃。解决办法当然是在同一个程序中不要有同一个类的多个版本。这通常通过在单个 header (或非 API/impl 类的实现)中定义每个类并在需要类定义的地方包含该 header 来实现。

原回答: 如果它在除一个地方之外的任何地方都有效,那么听起来对象在那个地方无效(作为派生指针而不是基础听起来很像你进入了未定义行为的领域)。它可能是损坏的内存、已删除的对象指针或其他原因。最好的选择是您是否可以在其上运行 valgrind。

关于c++ - 同名类之间的共享 vtables : call to virtual method crashes when casting to base type,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/8851931/

相关文章:

c++ - 结构成员访问 - 命令行

c++ - 模板类中的双嵌套类前向声明

c++ - 复制构造函数中的指针

c++ - 将 std::istringstream 作为参数传递无效

c++ - 将虚函数添加到子类而不在子类中?

c++ - 统一码灾难! Ms-Access 97 迁移到 Ms-Access 2007

c++ - 虚函数错误(vtable?)

c++ - 使用 operator new 从 C 实例化 C++ 对象

c++ - USB 大容量存储 linux

c++ - 从文件末尾检索字符串时失败