2013-07-28 56 views
27

幾年前,我正在嘗試重新學習C++後,介紹了一些課程,我有一些基本問題。我嘗試使用朋友功能時出現當前問題。這是我的代碼在2個文件中。函數的多個定義的錯誤

第一:

// fun.cpp 

#include <iostream> 
using namespace std; 

class classA { 
    friend void funct(); 
public: 
    classA(int a=1,int b=2):propa(a),propb(b){cout<<"constructor\n";} 
private: 
    int propa; 
    int propb; 
    void outfun(){ 
     cout<<"propa="<<propa<<endl<<"propb="<<propb<<endl; 
    } 
}; 
void funct(){      // ERROR HERE 
    cout<<"enter funct"<<endl; 
    classA tmp(1,2); 
    tmp.outfun(); 
    cout<<"exit funct"<<endl; 
} 

二:

// mainfile.cpp 
#include <iostream> 
#include "fun.cpp" 
using namespace std; 

int main(int nargin,char* varargin[]) { 
    cout<<"call funct"<<endl; 
    funct(); 
    cout<<"exit main"<<endl; 
    return 0; 
} 

我正的錯誤是 「本功能的`多個定義()'」。當我將它聲明爲朋友函數時,我使用了錯誤的語法嗎?

回答

18

問題是,如果您的程序中的兩個地方包含fun.cpp,您最終會定義它兩次,這是無效的。

您不想包含cpp文件。你想包含頭文件。

頭文件應該只有類定義。將分別編譯的相應cpp文件將具有函數定義。

fun.hpp:

#include <iostream> 

class classA { 
    friend void funct(); 
public: 
    classA(int a=1,int b=2):propa(a),propb(b){std::cout<<"constructor\n";} 
private: 
    int propa; 
    int propb; 
    void outfun(){ 
     std::cout<<"propa="<<propa<<endl<<"propb="<<propb<< std::endl; 
    } 
}; 

fun.cpp:

#include "fun.hpp" 

using namespace std; 

void funct(){ 
    cout<<"enter funct"<<endl; 
    classA tmp(1,2); 
    tmp.outfun(); 
    cout<<"exit funct"<<endl; 
} 

mainfile.cpp:

#include <iostream> 
#include "fun.hpp" 
using namespace std; 

int main(int nargin,char* varargin[]) { 
    cout<<"call funct"<<endl; 
    funct(); 
    cout<<"exit main"<<endl; 
    return 0; 
} 

需要注意的是,通常建議避免using namespace std頭文件。

+1

此外,它可以與一些連接包裹頭衛士幫助 - 上的#ifndef –

+0

@CarlNorum我的印象是,多個定義錯誤是鏈接錯誤,不是編譯搜索。但也許我錯了。 –

+2

他們是,但頭衛隊沒有任何關係。那麼,除非你在標題中做了一些瘋狂的事情,那就是。 –

37

這是一個高度簡化但有希望的相關視圖,說明在C++中構建代碼時會發生什麼。

C++在拆分以下不同階段產生的機器可執行代碼的負載 -

  1. 預處理 - 這是任何宏 - #defines等,你可能會使用得到擴展。

  2. 編譯 - 將每個cpp文件以及該文件中所有的#include d文件直接或間接地(一起稱爲編譯單元)轉換爲機器可讀目標代碼。

    這是C++還檢查所有功能而定義的(即含有一個主體中{}例如
    void Foo(int x){ return Boo(x); })指的其它功能中的有效方式。

    它該方法是通過堅持認爲你至少要提供這些其他函數的聲明(例如void Boo(int);),以便它可以檢查您是否正確調用了其他函數,可以直接在調用它的cpp文件中進行,也可以通過包含頭文件

    請注意,只有t他的機器代碼對應於在此cpp中定義的函數,並且包含的​​文件被構建爲該編譯單元的對象(二進制)版本(例如, Foo),而不是僅僅聲明的那些(例如Boo)。

  3. 鏈接 - 這是C++去打獵的東西,聲明和調用在每個編譯單元,並將其鏈接到它獲取調用的地方階段。現在,如果沒有發現此功能的定義,則鏈接器將放棄並輸出錯誤。同樣,如果發現同一個函數簽名的多個定義(基本的名稱和參數類型需要),也出現了錯誤,因爲它認爲它含糊不清,不希望挑選一個任意。

後者是你的情況發生了什麼。這樣做的fun.cpp文件的#include,既fun.cppmainfile.cppfunct()定義和鏈接器不知道在你的程序中使用哪一個和抱怨它。

上面提到的定位點作爲斯沃恩是不包括具有funct()定義cpp文件中mainfile.cpp和代替移動的funct()聲明在一個單獨的頭文件和包括在mainline.cpp。這樣,編譯器將獲得的funct()聲明一起工作,並且鏈接器會從fun.cpp得到funct()只是一個定義,並放心地使用它。