問題描述
如果兩個 C++ 文件對同名類的定義不同,那么在編譯和鏈接時,即使沒有警告也會拋出一些東西.例如,
If two C++ files have different definitions of classes with the same name, then when they are compiled and linked, something is thrown out even without a warning. For example,
// a.cc
class Student {
public:
std::string foo() { return "A"; }
};
void foo_a()
{
Student stu;
std::cout << stu.foo() << std::endl;
}
// b.cc
class Student {
public:
std::string foo() { return "B"; }
};
void foo_b()
{
Student stu;
std::cout << stu.foo() << std::endl;
}
當使用 g++ 編譯和鏈接在一起時,兩者都會輸出A"(如果在命令行順序中 a.cc 在 b.cc 之前).
When compiled and linked together using g++, both will output "A" (if a.cc precedes b.cc in the command line order).
一個類似的主題是這里.我看到命名空間將解決這個問題,但我不知道為什么鏈接器甚至不發出警告.如果該類的一個定義具有另一個未定義的額外函數,假設 b.cc 更新為:
A similar topic is here. I see namespace will solve this problem but I don't know why the linker doesn't even shoot a warning. And if one definition of the class has extra function that isn't defined in another, say if b.cc is updated as:
// b.cc
class Student {
public:
std::string foo() { return "B"; }
std::string bar() { return "K"; }
};
void foo_b()
{
Student stu;
std::cout << stu.foo() << stu.bar() << std::endl;
}
然后 stu.bar() 運行良好.感謝任何能告訴我編譯器和鏈接器在這種情況下如何工作的人.
Then stu.bar() works well. Thanks to anyone who can tell me how the compiler and linker work in such situation.
另外一個問題,如果類是在頭文件中定義的,它們是否應該總是用未命名的命名空間包裝以避免這種情況?有副作用嗎?
As an extra question, if classes are defined in header files, should they always be wrapped with unnamed namespace to avoid such situation? Is there any side effects?
推薦答案
這違反了一個定義規則(C++03, 3.2/5 "One definition rule"),其中說(除其他外):
This is a violation of the one definition rule (C++03, 3.2/5 "One definition rule"), which says (among other things):
一個類類型可以有多個定義(第 9 條),...在一個程序中,只要每個定義出現在不同的翻譯單元,并提供定義滿足以下條件要求.給定這樣一個名為 D 的實體,定義在多個翻譯單元,然后
There can be more than one definition of a class type (clause 9), ... in a program provided that each definition appears in a different translation unit, and provided the definitions satisfy the following requirements. Given such an entity named D defined in more than one translation unit, then
- D 的每個定義都應由相同的記號序列組成;
如果你違反了一個定義規則,行為是未定義的(這意味著可能會發生奇怪的事情).
If you violate the one definition rule, the behavior is undefined (which means that strange things can happen).
鏈接器看到多個 Student::foo()
的定義——一個在 a 的目標文件中,一個在 b 的目標文件中.然而,它并沒有抱怨這一點;它只是選擇兩者之一(碰巧,它遇到的第一個).這種對重復函數的軟"處理顯然只適用于內聯函數.對于非內聯函數,鏈接器會抱怨多個定義并拒絕生成可執行文件(可能有選項可以放寬此限制).GNU ld
和 MSVC 的鏈接器都以這種方式運行.
The linker sees multiple definitions of Student::foo()
- one in a's object file and one in b's. However it doesn't complain about this; it just selects one of the two (as it happens, the first one it comes across). This 'soft' handling of duplicate functions apparently happens only for inline functions. For non-inline functions, the linker will complain about multiple definitions and will refuse to produce an executable (there may be options that relax this restriction). Both GNU ld
and MSVC's linker behave this way.
這種行為是有道理的;內聯函數需要在它們使用的每個翻譯單元中可用.在一般情況下,它們需要有非內聯版本可用(以防調用未內聯或函數地址被占用).inline
實際上只是圍繞單一定義規則的免費傳遞 - 但要使其起作用,所有內聯定義都需要相同.
The behavior makes some sense; inline functions need to be available in every translation unit they're used in. And in the general case they need to have non-inline versions available (in case the call isn't inlined or if the function's address is taken). inline
is really just a free pass around the one-definition rule - but for it to work, all the inline definitions need to be the same.
當我查看目標文件的轉儲時,我沒有看到任何明顯的東西向我解釋鏈接器如何知道允許一個函數具有多個定義而其他函數則不允許,但我確定有一些標志或記錄就是這樣做的.不幸的是,我發現鏈接器的工作原理和目標文件的詳細信息并沒有特別詳細的記錄,因此確切的機制可能對我來說仍然是個謎.
When I look at dumps of the object files, I don't see anything obvious that explains to me how the linker knows that one function is permitted to have multiple definitions and others aren't, but I'm sure there's some flag or record which does just that. Unfortunately, I find that the workings of the linker and object file details aren't particularly well documented, so the precise mechanism will probably remain a mystery to me.
至于你的第二個問題:
作為一個額外的問題,如果在頭文件中定義了類,應該它們總是用未命名的命名空間包裹以避免這種情況?有副作用嗎?
As an extra question, if classes are defined in header files, should they always be wrapped with unnamed namespace to avoid such situation? Is there any side effects?
您幾乎肯定不想這樣做,每個類在每個翻譯單元中都是一個不同的類型,因此從技術上講,不能將類的實例從一個翻譯單元傳遞到另一個(通過指針、引用或復制)).此外,您最終會得到任何靜態成員的多個實例.那可能不會奏效.
You almost certainly don't want to do this each class would be a distinct type in each translation unit, so technically instances of the class they couldn't be passed from one translation unit to another (by pointer, reference or copying). Also, you'd end up with multiple instances of any static members. That probably wouldn't work well.
將它們放在不同的命名空間中.
Put them in different, named namespaces.
這篇關于不同 C++ 文件中的相同類名的文章就介紹到這了,希望我們推薦的答案對大家有所幫助,也希望大家多多支持html5模板網!