2010-04-28 8 views
23

Étant donné cet exemple de code:définition multiples dans le fichier d'en-tête

complex.h:

#ifndef COMPLEX_H 
#define COMPLEX_H 

#include <iostream> 

class Complex 
{ 
public: 
    Complex(float Real, float Imaginary); 

    float real() const { return m_Real; }; 

private: 
    friend std::ostream& operator<<(std::ostream& o, const Complex& Cplx); 

    float m_Real; 
    float m_Imaginary; 
}; 

std::ostream& operator<<(std::ostream& o, const Complex& Cplx) { 
    return o << Cplx.m_Real << " i" << Cplx.m_Imaginary; 
} 
#endif // COMPLEX_H 

complex.cpp:

#include "complex.h" 

Complex::Complex(float Real, float Imaginary) { 
    m_Real = Real; 
    m_Imaginary = Imaginary; 
} 

main.cpp:

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

int main() 
{ 
    Complex Foo(3.4, 4.5); 
    std::cout << Foo << "\n"; 
    return 0; 
} 

Lors de la compilation de ce code, je reçois l'erreur suivante:

multiple definition of operator<<(std::ostream&, Complex const&) 

J'ai trouvé que le fait de cette fonction inline résout le problème, mais je ne comprends pas pourquoi. Pourquoi le compilateur se plaint-il de la définition multiple? Mon fichier d'en-tête est protégé (avec #define COMPLEX_H). Et, si elle se plaint de la fonction operator<<, pourquoi ne pas se plaindre de la fonction public real(), qui est également définie dans l'en-tête?

Et existe-t-il une autre solution en plus d'utiliser le mot-clé inline?

+0

Vous pouvez également rendre la fonction statique. Le spécificateur en ligne est couramment utilisé pour forcer une fonction à avoir une liaison interne. – Akanksh

+0

@Akanksh, en fait c'est exactement ce que "inline" est pour. –

+0

@Akanksh: L'utilisation de 'static' dans ce but est obsolète en C++. 'static' a été complètement remplacé par des espaces de noms anonymes, bien que dans ce cas particulier,' inline' soit le chemin à parcourir. –

Répondre

36

Le problème est que le morceau de code suivant est une définition, une déclaration:

std::ostream& operator<<(std::ostream& o, const Complex& Cplx) { 
    return o << Cplx.m_Real << " i" << Cplx.m_Imaginary; 
} 

vous pouvez marquer la fonction ci-dessus et de le rendre « en ligne » de sorte que plusieurs unités de traduction peut définir:

inline std::ostream& operator<<(std::ostream& o, const Complex& Cplx) { 
    return o << Cplx.m_Real << " i" << Cplx.m_Imaginary; 
} 

Ou vous pouvez simplement déplacer le définition d'origine de la fonction dans le fichier source "complex.cpp".

Le compilateur ne se plaint pas de "real()" car il est implicitement inline (toute fonction membre dont le corps est donné dans la déclaration de classe est interprétée comme si elle avait été déclarée "inline"). Les protecteurs du préprocesseur empêchent votre en-tête d'être inclus plus d'une fois à partir d'une seule unité de traduction (fichier source "* .cpp") .Cependant, les deux unités de traduction voient le même fichier d'en-tête. "main.o" (y compris les définitions données dans les en-têtes inclus par "main.cpp"), et le compilateur compile séparément "complex.cpp" en "complex.o" (y compris les définitions données dans les en-têtes inclus par "complexe .cpp "). Ensuite, l'éditeur de liens fusionne" main.o "et" complex.o "en un seul fichier binaire, c'est à ce point que l'éditeur de liens trouve deux définitions pour une fonction du même nom. point que l'éditeur de liens tente de résoudre des références externes (par exemple, "main.o" fait référence à "Complex :: Complex" mais n'a pas de définition pour cette fonction ... l'éditeur de liens localise la définition de "complex.o" et résout cette référence).

5

mise en œuvre Déplacer à complex.cpp

En ce moment, y compris après cette mise en œuvre du fichier est compilé à chaque fichier. Plus tard au cours de la liaison, il y a un conflit évident en raison des implémentations en double.

:: real() n'est pas signalé parce qu'il est en ligne implicitement (mise en œuvre dans la définition de classe)

6

And is there another solution as using the inline keyword?

Oui, il y a. Outre la forme définissant la méthode dans le fichier d'implémentation complex.cpp comme mentionné par d'autres, vous pouvez également mettre la définition dans un espace de noms sans nom.

namespace { 
    std::ostream& operator<<(std::ostream& o, const Complex& Cplx) { 
     return o << Cplx.m_Real << " i" << Cplx.m_Imaginary; 
    } 
} 

Dans la pratique, cela va créer un espace de noms unique pour chaque unité de compilation. De cette façon, vous évitez les conflits de noms. Cependant, les noms sont toujours exportés à partir de l'unité de compilation mais sont inutiles (puisque les noms sont inconnus).

Mettre la définition dans le fichier d'implémentation est souvent une meilleure solution. Toutefois, pour les modèles de classe, vous ne pouvez pas le faire car les compilateurs C++ ne supportent pas l'instanciation de modèles dans une unité de compilation différente de celle dans laquelle ils ont été définis. Dans ce cas, vous devez utiliser inline ou un espace de noms sans nom.

+1

Cela présente l'inconvénient majeur que chaque unité de traduction incluant cet en-tête obtient sa propre copie de l'implémentation. Y a-t-il quelque chose qui me manque ou est-ce vraiment aussi stupide qu'il me semble? – sbi

+1

@sbi: la même chose est vraie pour 'inline' (et ce n'est pas grave). Alors oui, il te manque quelque chose. - Pour les classes simples, tout mettre à l'intérieur du fichier d'implémentation est généralement meilleur. Mais pour les modèles, vous n'avez pas le choix (voir la mise à jour de la réponse). –

+0

Mais avec 'inline' j'ai au moins (heureusement, je sais) l'avantage de ne pas avoir un appel de fonction lors de l'invocation du code. Avec votre exemple, je ne vois aucun avantage, sauf pour le fait qu'un fichier cpp n'est pas nécessaire. Et ce serait pris en charge par «inline» très bien. – sbi

0

J'avais ce problème, même après que mon fichier source et en-tête aient été corrects.

Il s'est avéré que Eclipse utilisait des artefacts périmés d'une construction antérieure (en échec).

Pour corriger, utilisez Project > Clean puis reconstruisez.