在我们公司,直到最近,我们还没有使用名称空间,因为有些编译器无法很好地支持它们。
这会导致多次出现以下错误:
file_A.cpp
class Node {
Data *ptr;
Node() { ptr = new Data; }
~Node() { delete ptr; }
};
file_B.cpp
class Node {
vector<int> v;
Point *pt;
Node(int x,int y) { pt = new Point(x,y); v.push_back(0); }
~Node() { delete pt; }
};
void foo() {
Node n(10,10);
...
} // calls file_B::~Node() !!!
每个作者Node
都不知道另一个Node
的存在,但由于他希望这个类名可能被重用,所以他没有用它创建一个.hpp文件。
编译器以静默方式删除其中一个析构函数,因为它们的签名匹配,并且很难找到错误,因为它可能无法在不同的计算机中复制。
一旦发现错误,人们逐渐意识到错误,并且他们试图在未命名的命名空间中密封定义,或者避免在类的主体中隐藏成员函数[见下文] 。
问题1:既然你不能相信程序员会永远记得防御性地编程,那么是否有一种可以检测这些“非预期的弱链接符号”的工具?
意外我的意思是, .hpp 文件中的Node
类不,并且至少有一个类成员没有在类定义之间匹配......
问题2:如果我们不使用命名空间,但我们做内联每个函数,是否有可能出现自动生成的函数(复制) -ctor,copy-assignment,析构函数)会创建上面提到的“弱链接bug”?
方式1:包含在未命名的名称空间
中namespace {
class Node {
Data *ptr;
Node() { ptr = new Data; }
~Node() { delete ptr; }
};
}
方式2:避免内联
class Node {
Data *ptr;
Node();
~Node();
};
Node::Node() { ptr = new Data; }
Node::~Node() { delete ptr; }
答案 0 :(得分:2)
如果您的代码库足够大以证明这一努力,您可以自定义现有编译器来解决您的问题:
答案 1 :(得分:2)
"C++ and the linker"是一个非常有趣的读物。具体请参阅名为'Rules Without Enforcement Mean Nothing'的部分。
一个见解是,您可以通过解析目标文件并查找“W”来检测“弱”符号:
$ nm -C foo.o | grep doSomething
00000000 W doSomething()
因此,您可以添加后处理步骤,自动收集这些步骤并列出重复项。您可以将这些与预期重复项的主列表进行比较,如果有任何新副本,则引发标记。
另一种选择可能是gcc的-Fno-weak
option.从文档中不清楚复制品会发生什么,但查找它可能会很有趣。
链接的文章也回答了你的第二个问题(“上面的现象”是指删除一个重复的弱符号的一个实例):
在某些情况下,编译器必须创建符号 虽然它概括了功能。这可能发生在例如a时 函数指针引用该函数。那么,以上现象 启用优化后,并不总是消失。