C++ class 合并,链接器问题
C++ class merging, linker issue
让我们考虑一下我今天通过浏览一个相当大的代码库发现的以下情况(为了示例而举例说明真实源代码):
其中一个团队创建了以下两个文件和一组接口(MoreBase
、Base
)。到目前为止一切都很好。
* file1.h *
class MoreBase
{
public:
MoreBase();
virtual ~MoreBase();
};
class Base : public MoreBase
{
public:
virtual ~Base();
virtual void func() const = 0;
};
class A : public Base
{
public:
A();
~A();
virtual void func() const;
};
* file1.cpp *
#include <iostream>
#include "file1.h"
using namespace std;
MoreBase::MoreBase() { cout << "file 1 MoreBase::MoreBase " << (void*)this << endl; }
MoreBase::~MoreBase() { cout << "file 1 ~MoreBase::MoreBase " << (void*)this << endl;}
Base::~Base() { cout << "file 1 ~Base::Base " << (void*)this << endl; }
A::~A() { cout << "file1 ~A::A() "<< (void*)this << endl; }
A::A() { cout << "file 1 A::A() "<< (void*)this << endl; }
void A::func() const { cout << "file 1 A::func() "<< (void*)this << endl; }
但是还有另一个团队,在完全不同的部门、建筑、国家、大陆,正在开发完全不同的东西……例如:
* file2.h *
int some2method();
* file2.cpp *
#include <iostream>
using namespace std;
class Base
{
public:
virtual ~Base();
virtual void something() const = 0;
};
class B : public Base
{
public:
B();
~B();
virtual void something() const;
};
B::~B() { cout << "file 2 B::~B() "<< (void*)this << endl; }
B::B() { cout << "file 2 B::B() "<< (void*)this << endl; }
void B::something() const { cout << "file 2 B::something() "<< (void*)this << endl; }
// VARIABLE
const Base& x = B(); // ***
int some2method()
{
x.something();
return 42;
}
* main.cpp *
#include "file2.h"
int main()
{
some2method();
}
让我们像这样编译它:
$ g++ -ggdb main.cpp file1.cpp file2.cpp -o test
让我们运行:
$ ./test
file 1 MoreBase::MoreBase 0x6022f0
file 2 B::B() 0x6022f0
file 2 B::something() 0x6022f0
file 2 B::~B() 0x6022f0
file 1 ~Base::Base 0x6022f0
file 1 ~MoreBase::MoreBase 0x6022f0
你不觉得它构造了一个我从未请求过的 MoreBase
对象很奇怪吗?
可惜(1)公司开发一款产品,所以所有的源文件都链接成一个可执行文件。
不幸的是 (2) Base
在我们工作的域中是一个非常常见的名称...
幸运的是 (1) 在 some2method()
中找到了标有 // ***
的行,并且应用程序在编译时崩溃 海湾合作委员会。这就是我开始调查的地方(现在它在外面所以甚至没有崩溃)。
而且 显然 问题:这到底怎么可能?为什么链接器合并了两个完全不相关的 classes,尽管它们不幸共享相同的名称。这是未定义的行为、定义的东西还是仅仅是不幸?
编辑:我不要求解决问题(它可以用命名空间修复,或者简单地重命名不是public base class), 我只想知道根本原因:)
正如评论中提到的,这打破了一个定义规则,它需要 UB。防止这种情况的方法是使用命名空间,特别是将任何私有 class 定义(在 cpp 文件中读取)放在匿名命名空间中。
编辑:抱歉,我刚刚意识到,WhozCraig 已经在评论中提供了完全相同的答案
让我们考虑一下我今天通过浏览一个相当大的代码库发现的以下情况(为了示例而举例说明真实源代码):
其中一个团队创建了以下两个文件和一组接口(MoreBase
、Base
)。到目前为止一切都很好。
* file1.h *
class MoreBase
{
public:
MoreBase();
virtual ~MoreBase();
};
class Base : public MoreBase
{
public:
virtual ~Base();
virtual void func() const = 0;
};
class A : public Base
{
public:
A();
~A();
virtual void func() const;
};
* file1.cpp *
#include <iostream>
#include "file1.h"
using namespace std;
MoreBase::MoreBase() { cout << "file 1 MoreBase::MoreBase " << (void*)this << endl; }
MoreBase::~MoreBase() { cout << "file 1 ~MoreBase::MoreBase " << (void*)this << endl;}
Base::~Base() { cout << "file 1 ~Base::Base " << (void*)this << endl; }
A::~A() { cout << "file1 ~A::A() "<< (void*)this << endl; }
A::A() { cout << "file 1 A::A() "<< (void*)this << endl; }
void A::func() const { cout << "file 1 A::func() "<< (void*)this << endl; }
但是还有另一个团队,在完全不同的部门、建筑、国家、大陆,正在开发完全不同的东西……例如:
* file2.h *
int some2method();
* file2.cpp *
#include <iostream>
using namespace std;
class Base
{
public:
virtual ~Base();
virtual void something() const = 0;
};
class B : public Base
{
public:
B();
~B();
virtual void something() const;
};
B::~B() { cout << "file 2 B::~B() "<< (void*)this << endl; }
B::B() { cout << "file 2 B::B() "<< (void*)this << endl; }
void B::something() const { cout << "file 2 B::something() "<< (void*)this << endl; }
// VARIABLE
const Base& x = B(); // ***
int some2method()
{
x.something();
return 42;
}
* main.cpp *
#include "file2.h"
int main()
{
some2method();
}
让我们像这样编译它:
$ g++ -ggdb main.cpp file1.cpp file2.cpp -o test
让我们运行:
$ ./test
file 1 MoreBase::MoreBase 0x6022f0
file 2 B::B() 0x6022f0
file 2 B::something() 0x6022f0
file 2 B::~B() 0x6022f0
file 1 ~Base::Base 0x6022f0
file 1 ~MoreBase::MoreBase 0x6022f0
你不觉得它构造了一个我从未请求过的 MoreBase
对象很奇怪吗?
可惜(1)公司开发一款产品,所以所有的源文件都链接成一个可执行文件。
不幸的是 (2) Base
在我们工作的域中是一个非常常见的名称...
幸运的是 (1) 在 some2method()
中找到了标有 // ***
的行,并且应用程序在编译时崩溃 海湾合作委员会。这就是我开始调查的地方(现在它在外面所以甚至没有崩溃)。
而且 显然 问题:这到底怎么可能?为什么链接器合并了两个完全不相关的 classes,尽管它们不幸共享相同的名称。这是未定义的行为、定义的东西还是仅仅是不幸?
编辑:我不要求解决问题(它可以用命名空间修复,或者简单地重命名不是public base class), 我只想知道根本原因:)
正如评论中提到的,这打破了一个定义规则,它需要 UB。防止这种情况的方法是使用命名空间,特别是将任何私有 class 定义(在 cpp 文件中读取)放在匿名命名空间中。
编辑:抱歉,我刚刚意识到,WhozCraig 已经在评论中提供了完全相同的答案