簡體   English   中英

C ++ 03鏈接器“已定義的符號”未出現在中間文件中

[英]C++03 linker “already defined symbol” doesn't appear on intermediate file

我在visual studio 2005上的一個大型項目遇到了問題,我已經用完了很多想法。

我甚至無法提供一個有效的代碼片段,因為我不知道有什么相關,但我會嘗試:

我需要讓我的項目中的每個.cpp文件都有自己的ID號,並創建一個知道該ID的對象(可全局訪問)的實例。 我在此主題上接受了關於已接受答案的幫助如何在c ++中管理文件唯一ID並使其在沙箱環境中工作。

添加文件,為它們提供唯一的#define FILEID (FileId::ID_FileName)然后訪問它們的實例在沙盒上工作正常。

現在出現了麻煩 - 我粘貼了使文件知道他們的IDS的代碼到主項目,並編譯。

到現在為止還挺好。

現在,我添加到項目中現有的.cpp文件之一:

#include "ids.h"
#define FILEID File1   // The FileId corresponding to this file
#include "global.h"

仍然編譯,鏈接,一切都很好。

將這些行添加到項目中的(任意)第二個.cpp文件現在會出現鏈接錯誤:

其中:

  • name1:第一個文件我添加了行(alphabeticcaly)
  • name2:其他不相關的文件名(也可以是我添加行的第二個文件,但也可能只是其他文件)

in name2.obj : error LNK2005: "public static class Instance & __cdecl Manager<3>::getInstance(void)" (?getInstance@$Manager@$02@@SAAAVInstance@@XZ) already defined in name1.obj 的錯誤 in name2.obj : error LNK2005: "public static class Instance & __cdecl Manager<3>::getInstance(void)" (?getInstance@$Manager@$02@@SAAAVInstance@@XZ) already defined in name1.obj

有時錯誤僅在第二個文件中,有時(在沒有更改的連續構建之間)錯誤出現在文件夾中的每個.cpp文件中。

查看我添加行的文件的中間文件(預處理器輸出)顯示了該文件的一個外觀

template <>
Instance &Manager<FILEID>::getInstance()
{
    static Instance theInstance = getTheFactory().getInstance(FILEID);
    return theInstance;
};

使用正確的FileId :: ID_FileName,這是與其他文件不同的名稱。 仍然,鏈接器認為在多個文件中使用相同的FileId。

在不相關的文件(也給出完全相同的錯誤)上,根本沒有getInstance()出現。 顯然,聯系人不應該在那里大喊大叫。

我查了一下,沒有.cpp文件在項目的某個地方互相包含。

我完全沒有想到可能導致這種情況的想法,並希望得到任何幫助。


編輯1

ids.h

enum FileId{
    ID_file1ID=3,//just to see a non zero number in the debugger, which I do
    ID_file2ID,
    //and so on
    FileIdSize
}

編輯2

當這些錯誤開始時,編譯器開始表現得非常意外。

將行sdfsdfgasaedfahjk添加到任何文件仍然是編譯和通過。

它清楚地說明了已添加到該行的文件名以進行編譯。 它明確指出它與它的聯系。 它過去了。

我現在不能相信編譯器。

不知道發生了什么。

您有2個cpp文件將FILEID定義為相同的值3

至於MCVE:

ids.h:

#pragma once

#define File1 3
#define File2 3 //<--same value on purpose

global.h

struct Instance
{

};

struct Factory
{
    Instance getInstance(int FileID) { return Instance(); }
};

template <int ID>
struct Manager
{
    Factory factory;

    Instance& getInstance();
    Factory& getTheFactory() { return factory; }
};

template <>
Instance& Manager<FILEID>::getInstance()
{
    static Instance theInstance = getTheFactory().getInstance(FILEID);
    return theInstance;
};

name1.cpp

#include "ids.h"
#define FILEID File1   // The FileId corresponding to this file
#include "global.h"

name2.cpp

#include "ids.h"
#define FILEID File2   // The FileId corresponding to this file
#include "global.h"

在編譯時,為name1.cppname2.cpp創建了一個特殊的Manager<3>::getInstance(void) name2.cpp

您不能在2個不同的編譯單元中為FILEID使用相同的值。


編輯:編譯時檢查值

需要預處理器定義__BASE_FILE__="%(Filename)%(Extension)"

(配置屬性 - > C / C ++ - >預處理器 - >預處理器定義)

template <>
Instance& Manager<FILEID>::getInstance()
{
    #define _STR(x) #x
    #define STR(x) _STR(x)
    #define CHECK_ID() __pragma(message("Initializing \"Instance& Manager<FILEID>::getInstance()\" with FILEID="STR(FILEID)" in "STR(__BASE_FILE__)))
    CHECK_ID()
    static Instance theInstance = getTheFactory().getInstance(FILEID);
    return theInstance;
};

示例 - 輸出:

1>------Build started : Project : Test_Call, Configuration : Debug Win32------
1>  name1.cpp
1>  Initializing "Instance& Manager<FILEID>::getInstance()" with FILEID = FileId::ID_file1ID in "name1.cpp"
1>  name2.cpp
1>  Initializing "Instance& Manager<FILEID>::getInstance()" with FILEID = FileId::ID_file2ID in "name2.cpp"
1>  Test_Call.vcxproj-><Project>\Debug\Test_Call.exe
== == == == == Build: 1 succeeded, 0 failed, 0 up - to - date, 0 skipped == == == == ==

編輯:使用FileId值作為模板參數(MSVE)

id.h

#pragma once

enum FileId {
    ID_file1ID = 3,//just to see a non zero number in the debugger, which I do
    ID_file2ID,
    //and so on
    FileIdSize
};

global.h

#pragma once

#include "ids.h"

struct Instance
{

};

struct Factory
{
    Instance getInstance(int FileID) { return Instance(); }  
};

template <FileId ID>
struct Manager
{
    static const FileId manager_id = ID;        
    static Factory& getTheFactory() { return m_factory; }
    static Instance& getInstance()
    {
        static Instance theInstance = getTheFactory().getInstance(manager_id);
        return theInstance;
    }

private:
    static Factory m_factory;
};

global.cpp

#include "global.h"

Factory Manager<FileId::ID_file1ID>::m_factory;
Factory Manager<FileId::ID_file2ID>::m_factory;

name1.cpp

#include "global.h"

void test1()
{
    Instance& a = Manager<FileId::ID_file1ID>::getInstance();
}

name2.cpp

#include "global.h"

void test2()
{
    Instance& a = Manager<FileId::ID_file2ID>::getInstance();
}

TEST.CPP

#include <iostream>
#include "global.h"

using namespace std;


int main(int argc, char** argv)
{
    Instance& a = Manager<FileId::ID_file1ID>::getInstance();
    Instance& b = Manager<FileId::ID_file2ID>::getInstance();
    Instance& c = Manager<FileId::ID_file1ID>::getInstance();

    Instance* aptr = &a;
    Instance* bptr = &b;
    Instance* cptr = &c;

    printf("aptr==bptr -> %s\n", (aptr == bptr) ? "true" : "false"); //->false
    printf("aptr==cptr -> %s\n", (aptr == cptr) ? "true" : "false"); //->true (both use the instance from ID_file1ID
    printf("bptr==cptr -> %s\n", (bptr == cptr) ? "true" : "false"); //->false

}

這不是一個答案,但可能有助於找出問題所在。

  • 以下代碼與原始答案基本相同,但由於在各個地方需要樣板代碼的昂貴,所有復雜性都被剝奪了。

    idmanager.h

     struct Instance {/*...*/}; Instance &getFile1Instance(); Instance &getFile2Instance(); // etc... 

    idmanager.cpp

     Instance &getFile1Instance() { static Instance file1instance; return file1instance; } Instance &getFile2Instance() { static Instance file2instance; return file2instance; } // etc... 

    在每個文件中,放在開頭

     #include "idmanager.h" 

    並且您可以以顯而易見的方式獲取任何文件的靜態Instance

    這很簡單,因此將其復制到項目中不會導致問題。

  • 如果上面的示例有效,那么嘗試使其稍微接近原始答案:將getFileXInstance函數的定義移動到文件本身,並刪除idmanager.cpp

    idmanager.h

     struct Instance {/*...*/}; Instance &getFile1Instance(); Instance &getFile2Instance(); // etc... 

    file1.cpp

     #include "idmanager.h" Instance &getFile1Instance() { static Instance file1instance; return file1instance; } 

    file2.cpp

     // etc... 

    顯然,這只是在不同的.obj文件之間移動代碼,所以應該仍然有效。

  • 現在用帶有單個靜態成員函數getInstancestruct替換每個getFileXInstance函數,如下所示:

    idmanager.h

     struct Instance {/*...*/}; struct Manager1 { static Instance &getInstance(); // defined in file1.cpp }; struct Manager2 { static Instance &getInstance(); // defined in file2.cpp }; // etc... 

    file1.cpp

     #include "idmanager.h" Instance &Manager1::getInstance() { static Instance file1instance; return file1instance; } 

    file2.cpp

     // etc... 
  • 上一步允許我們使用模板減少樣板代碼的數量:

    idmanager.h

     struct Instance {/*...*/}; template <int id> struct Manager { static Instance &getInstance(); // each instantiation has its definition in a different cpp file }; 

    file1.cpp

     #include "idmanager.h" template <> Instance &Manager<1>::getInstance() { static Instance file1instance; return file1instance; } 

    這是鏈接器錯誤最有可能再次出現的地方,如果它們完全出現的話。

  • 通過將公共代碼放在共享頭globals.h ,並將預處理器常量FILEID給它,也可以刪除更多重復。

    idmanager.h

     struct Instance {/*...*/}; template <int id> struct Manager { static Instance &getInstance(); // each instantiation has its definition in a different cpp file }; 

    file1.cpp

     #include "idmanager.h" #define FILEID 1 #include "globals.h" 

    globals.h

     template <> Instance &Manager<FILEID>::getInstance() { static Instance theInstance; return theInstance; } 

最后一個示例現在與原始答案相同,但有一些差異(沒有工廠,沒有枚舉,沒有getThisFileInstance() )與鏈接器錯誤無關。 因此(假設第一個示例有效)您可以確定哪個更改破壞了程序,這應該有助於診斷真正的問題。

(注意:雖然你的錯誤正好是多個文件共享同一個id所出現的錯誤,但我認為這不是這種情況。)

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM