如果我在項目中有兩個源文件,每個文件都定義了一個具有相同名稱的類,那麼決定使用哪個類的哪個版本?什麼決定哪個類定義包含在兩個源文件中的同名命名類中?
例如:
// file1.cpp:
#include <iostream>
#include "file2.h"
struct A
{
A() : a(1) {}
int a;
};
int main()
{
// foo() <-- uncomment this line to draw in file2.cpp's use of class A
A a; // <-- Which version of class A is chosen by the linker?
std::cout << a.a << std::endl; // <-- Is "1" or "2" output?
}
...
//file2.h:
void foo();
...
// file2.cpp:
#include <iostream>
#include "file2.h"
struct A
{
A() : a(2) {}
int a;
};
void foo()
{
A a; // <-- Which version of class A is chosen by the linker?
std::cout << a.a << std::endl; // <-- Is "1" or "2" output?
}
我已經能夠得到A
不同版本的鏈接器選擇,使用相同的代碼 - 僅僅通過改變我輸入代碼的順序(沿途建立)。
當然,在相同名稱空間中使用相同名稱包含不同的類定義是一種糟糕的編程習慣。但是,是否有定義規則來確定鏈接器將選擇哪個類 - 如果是,它們是什麼?作爲這個問題的一個有用的附錄,我想知道(一般情況下)編譯器/鏈接器如何處理類 - 編譯器,當它構建每個源文件時,將類名和編譯類定義合併到目標文件,而鏈接器(在名稱衝突的場景中)拋出了一組編譯的類函數/成員定義?
名稱衝突的問題並非神祕 - 我現在意識到它每次都會發生一個只有頭文件的模板文件,它由兩個或多個源文件構成(隨後相同的模板類被實例化,並且相同的成員在這些多個源文件中稱爲函數),就像STL的一個常見場景一樣。每個源文件都必須具有相同的實例化模板類函數的單獨編譯版本,因此鏈接器必須在鏈接時間在這些函數的不同編譯版本中進行選擇),我想。
- 增編有關於Java相關的問題 -
我注意到,各種各樣的回答都表示對C的一個定義規則(http://en.wikipedia.org/wiki/One_definition_rule)++。作爲一個有趣的說法,我是否正確地認爲Java沒有這樣的規則 - 所以Java規範允許在Java中允許多個不同的定義?
這個標準事實上是否表示一個程序不允許提供同一類的不同定義?這讓我覺得這個標準不能說明這一點,因爲這是不可能實現的 - 例如,如果以後修改了兩個源文件中的一個並重建了程序,而是之前構建的第二個源文件的編譯目標文件與之前版本的編譯器相關聯。因此,標準中可能沒有規定禁止使用不同的類別定義?這似乎是一個困難的灰色地帶。 –
@DanNissenbaum:沒有歧義!該標準明確規定了一個定義規則:3.2 [basic.def.odr]第6段。然而,在評論中引用太長。這個定義的重點在於編譯器和/或鏈接器可能無法檢查約束是否被遵守。因此,該程序具有未定義的行爲。 –