2013-07-28 9 views
27

Próbuję ponownie nauczyć się języka C++ po odbyciu kursu wprowadzającego kilka lat temu i mam kilka podstawowych problemów. Mój obecny problem występuje podczas próby użycia funkcji znajomego. Oto mój kod w 2 plikach.Błąd z wieloma definicjami funkcji

pierwsze:

// 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; 
} 

drugie:

// 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; 
} 

Błąd jestem coraz to "wielokrotne definicja` FUNCT()". Czy używam niewłaściwej składni podczas deklarowania jej jako funkcji znajomego?

Odpowiedz

18

Problem polega na tym, że jeśli umieścisz fun.cpp w dwóch miejscach twojego programu, skończysz definiując go dwukrotnie, co nie jest poprawne.

Nie chcesz dołączać plików cpp. Chcesz dołączyć pliki nagłówkowe.

Plik nagłówkowy powinien mieć definicję klasy. Odpowiedni plik cpp, który skompilujesz osobno, będzie miał definicję funkcji.

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; 
} 

Należy pamiętać, że to jest zalecane, aby uniknąć using namespace std w plikach nagłówkowych.

+1

Również może pomóc niektórym linkerom w zawijaniu osłon nagłówków - wyszukiwanie w #ifndef –

+0

@CarlNorum Miałem wrażenie, że błędy wielu definicji nie były kompilacją błędów linkerów. Ale może się mylę. –

+2

Są, ale strażnik nagłówka nie ma z tym nic wspólnego. Cóż, chyba że robisz coś szalonego w swoich nagłówkach. –

37

Oto bardzo uproszczony, ale mam nadzieję, trafny pogląd na to, co dzieje się, gdy budujesz swój kod w C++.

C++ dzieli obciążenie maszyny generujące kod wykonywalny w następujący różne fazy -

  1. Przetwarzanie wstępne - To gdzie wszelkie makra - #defines etc Być może korzystasz się rozszerzył.

  2. Kompilacja - każdy plik cpp wraz z wszystkimi plikami #include d w tym pliku bezpośrednio lub pośrednio (zwane łącznie jednostką kompilacji) jest konwertowany na kod obiektowy do odczytu maszynowego.

    To gdzie C++ sprawdza również, że wszystkie funkcje zdefiniowane (czyli zawierająca ciało {} np
    void Foo(int x){ return Boo(x); }) odnosimy się do innych funkcji w prawidłowy sposób.

    sposób to robi, że jest przez twierdząc, że ciebie zapewnić co najmniej deklarację tych innych funkcji (np void Boo(int);) przed wywołaniem go więc może sprawdzić, czy nazywając ją prawidłowo między innymi. można to zrobić bezpośrednio w pliku cpp, gdzie jest nazywany lub zwykle w dołączonym plik nagłówkowy:

    Uwaga: tylko t on kod maszynowy, który odpowiada funkcji określonych w niniejszej CPP i dołączonych plików zostanie zbudowany jako obiekt (binarnym) tej jednostki kompilacji (np Foo), a nie te, które są jedynie zadeklarowane (np. Boo).

  3. Łączenie - jest to etap, w którym C++ wyszukuje rzeczy zadeklarowane i wywoływane w każdej jednostce kompilacji i łączy je z miejscami, w których jest wywoływana. Teraz, jeśli nie znaleziono żadnej definicji tej funkcji, linker rezygnuje z błędów. Podobnie, jeśli znajdzie wiele definicji tej samej sygnatury funkcji (w zasadzie nazwy i typów parametrów, które pobiera), również pomija błędy, ponieważ uważa je za niejednoznaczne i nie chce wybierać jednego z nich arbitralnie.

To ostatnie dotyczy tego, co dzieje się w twoim przypadku. Wykonanie pliku #include z pliku fun.cpp powoduje, że zarówno fun.cpp, jak i mainfile.cpp mają definicję funct(), a linker nie wie, którego użyć w programie i narzeka na nią.

Rozwiązaniem jak Vaughn których mowa powyżej, nie obejmują plik cpp z definicją funct() w mainfile.cpp i zamiast przesunąć deklarację funct() w oddzielnym pliku nagłówka i to, że w mainline.cpp. W ten sposób kompilator otrzyma deklarację funct(), z którą będzie mógł pracować, a linker uzyska tylko jedną definicję funct() z fun.cpp i użyje go z pewnością.

Powiązane problemy