问候,
我遇到了一个奇怪的seg故障问题。我的应用程序在运行时转储核心文件。在深入研究之后,我发现它在这个区块中死了:
#include <lib1/c.h>
...
x::c obj;
obj.func1();
我在库lib1中定义了类c:
namespace x
{
struct c
{
c();
~c();
void fun1();
vector<char *> _data;
};
}
x::c::c()
{
}
x::c::~c()
{
for ( int i = 0; i < _data.size(); ++i )
delete _data[i];
}
直到我在lib1.so文件上运行nm时,我才弄明白:我的函数定义多于我定义的函数:
x::c::c()
x::c::c()
x::c::~c()
x::c::~c()
x::c::func1()
x::c::func2()
在代码库中搜索后,我发现其他人在同一名称空间中定义了一个具有相同名称的类,但在另一个库lib2中定义如下:
namespace x
{
struct c
{
c();
~c();
void func2();
vector<string> strs_;
};
}
x::c::c()
{
}
x::c::~c()
{
}
我的应用程序链接到lib2,它依赖于lib1。这个有趣的行为带来了几个问题:
为什么它会起作用?我期望在链接lib2(取决于lib1)时出现“多个定义”错误,但从未有过这样的错误。该应用程序似乎正在执行func1中定义的内容,除非它在运行时转储核心。
附加调试器后,我发现我的应用程序在lib2中调用了c类的ctor,然后调用func1(在lib1中定义)。当超出范围时,它会在lib2中调用c类的dtor,其中发生seg错误。任何人都可以教我怎么会发生这种情况?
如何防止此类问题再次发生?我可以使用任何C ++语法吗?
忘记提及我在RHEL4上使用g ++ 4.1,非常感谢!
答案 0 :(得分:1)
1
不必由编译器诊断违反“一个定义规则”。实际上,当您将多个目标文件链接在一起时,它们通常只会在链接时被识别。
在链接时,有关原始类定义的信息可能不再存在(在编译器步骤之后不需要它们),因此具有多个类定义通常不容易标记给用户。
2
一旦你有两个不同的定义,几乎任何事情都可能发生,你就处于未定义的行为领域。无论发生什么,这都是可能的结果。
3
最明智的做法是与团队中的其他成员进行沟通。同意谁将使用哪些命名空间,您将不会遇到这些问题。否则,您将指向整个项目的文档工具或静态分析工具。许多此类工具将能够诊断多个不一致的类定义。
答案 1 :(得分:0)
只是一个猜测,但我没有看到任何using namespace x;
所以也许它使用了一个命名空间而不是另一个?
答案 2 :(得分:0)
随着模板的出现,有必要允许具有相同名称的代码体的多个定义;编译器无法知道是否已在另一个编译单元(即源文件)中生成相同的模板代码。当链接器找到这些重复项时,它假定它们是相同的。您需要确保它们的负担 - 这称为One Definition Rule。
答案 3 :(得分:0)
在链接器级别,这是库插入。遗憾的是,有效符号绑定取决于链接器命令行上的目标文件的顺序(这是叹息,历史)。
根据您的描述,lib1
首先出现在链接器参数列表中,lib2
位于第二个,而插入来自lib1
的符号。这解释了lib2
对构造函数和析构函数的调用,但是从func1
调用了lib1
(因为在func1
中没有lib2
- 派生符号,所以有没有“隐藏”,呼叫绑定到lib1
。)
此特定问题的解决方案是在链接器调用命令上反转库的顺序。
答案 4 :(得分:0)
关于一个定义规则有很多答案。但是,对我而言,这看起来更像是一个缺少复制的构造函数。
详细说明:
如果在对象上调用了复制构造函数,那么就会出现内存泄漏。这是因为删除将在同一组指针上调用两次。
namespace x
{
struct c
{
c() {
}
~c() {
for ( int i = 0; i < _data.size(); ++i )
delete _data[i];
}
c(const c & rhs) {
for (int i=0; i< rhs.size(); ++i) {
int len = strlen(rhs[i]);
char *mem = malloc(len + 1);
strncpy(mem, rhs[i], len + 1);
_data.push_back(mem);
}
void fun1();
vector<char *> _data;
};
}