检查下面的更新,我可以重现并需要帮助。
我有一个奇怪的崩溃,其中一些方法在除 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...
最佳答案
编辑响应更新:
在您使用 mod1
和 mod2
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/