2008-10-17 5 views
25

Ich habe eine C++ Header-Datei mit einer Klasse. Ich möchte in mehreren Projekten diese Klasse verwenden, bu Ich möchte nicht, eine separate Bibliothek für sie schaffen, so dass ich beide Methoden Erklärungen und Definitionen in der Header-Datei setzen:mehrere Definition Fehler einschließlich C++ Header-Datei mit Inline-Code aus mehreren Quellen

// example.h 
#ifndef EXAMPLE_H_ 
#define EXAMPLE_H_ 
namespace test_ns{ 

class TestClass{ 
public: 
    void testMethod(); 
}; 

void TestClass::testMethod(){ 
    // some code here... 
} 

} // end namespace test_ns 
#endif 

Wenn innerhalb der gleiches Projekt, das ich diesen Header von mehr als eine CPP-Datei enthalten, erhalte ich eine Fehlermeldung, „multiple definition of test_ns::TestClass::testMethod()“, während, wenn ich die Methodendefinition innerhalb der Klasse Körper setze dies nicht geschieht:

// example.h 
#ifndef EXAMPLE_H_ 
#define EXAMPLE_H_ 
namespace test_ns{ 

class TestClass{ 
public: 
    void testMethod(){ 
     // some code here... 
    } 
}; 

} // end namespace test_ns 
#endif 

Da die Klasse definiert ist in einem Namespace, sollten die beiden Formen nicht äquivalent sein? Warum wird die Methode im ersten Fall doppelt definiert?

Antwort

20

Diese sind nicht gleichwertig. Das zweite Beispiel hat einen impliziten "Inline" -Modifikator für die Methode und so wird der Compiler mehrere Definitionen selbst abstimmen (höchstwahrscheinlich mit interner Verknüpfung der Methode, wenn sie nicht inlineable ist).

Das erste Beispiel ist nicht inline, und wenn dieser Header in mehreren Übersetzungseinheiten enthalten ist, dann haben Sie mehrere Definitionen und Linker-Fehler.

Außerdem sollten Header wirklich immer geschützt werden, um mehrere Definitionsfehler in der gleichen Übersetzungseinheit zu verhindern. Das sollte Ihren Header konvertieren:

#ifndef EXAMPLE_H 
#define EXAMPLE_H 

//define your class here 

#endif 
+0

Danke für den Hinweis ... Ich hatte die Include-Wächter im Beispiel vergessen (aber nicht im eigentlichen Code). –

20

Innerhalb des Klassenkörpers wird vom Compiler als Inline betrachtet. Wenn Sie außerhalb des Körpers implementieren, aber immer noch im Header, müssen Sie die Methode explizit als 'Inline' markieren.

namespace test_ns{ 

class TestClass{ 
public: 
    inline void testMethod(); 
}; 

void TestClass::testMethod(){ 
    // some code here... 
} 

} // end namespace test_ns 

bearbeiten

Für mich hilft es oft diese Art von kompilieren Probleme zu lösen, indem sie erkennen, dass der Compiler nicht so etwas wie eine Header-Datei zu sehen ist. Header-Dateien werden vorverarbeitet und der Compiler sieht nur eine riesige Datei, die jede Zeile von jeder (rekursiv) enthaltenen Datei enthält. Normalerweise ist der Startpunkt für diese rekursiven Includes eine cpp-Quelldatei, die kompiliert wird. In unserem Unternehmen kann sogar eine bescheiden aussehende cpp-Datei dem Compiler als 300000-Zeilenmonster präsentiert werden.

Wenn also eine Methode, die nicht inline deklariert ist, in einer Header-Datei implementiert ist, könnte der Compiler in der vorverarbeiteten Datei Dutzende Male void TestClass :: testMethod() {...} sehen. Jetzt können Sie sehen, dass dies keinen Sinn ergibt, genauso wie beim Kopieren/Einfügen in eine Quelldatei. Und selbst wenn es Ihnen gelang, es nur einmal in jeder Kompilierungseinheit zu haben, durch irgendeine Form der bedingten Kompilierung (z. B. unter Verwendung von Einschlussklammern), würde der Linker das Symbol dieser Methode trotzdem in mehreren kompilierten Einheiten (Objektdateien) finden.

3

keine Funktion/Methodendefinition in einer Header-Datei setzen Sie, wenn sie inlined werden (indem man sich direkt in einer Klassendeklaration oder explizit angegeben durch das Schlüsselwort inline definieren)

Header-Dateien sind (meistens) für die Deklaration (was auch immer Sie deklarieren müssen). Die erlaubten Definitionen sind die für Konstanten und inlined Funktionen/Methoden (und auch Templates).

1

Ihr erstes Code-Snippet fällt mit C++ "One Definition Rule" - see here for a link to a Wikipedia article describing ODR. Sie fallen tatsächlich Punkt # 2, weil jedes Mal, wenn der Compiler die Header-Datei in eine Quelldatei enthält, Sie das Risiko der Compiler erzeugt eine global sichtbare Definition von test_ns::TestClass::testMethod(). Und natürlich, wenn Sie den Code verlinken, wird der Linker Kätzchen haben, weil er das gleiche Symbol in mehreren Objektdateien finden wird.

Der zweite Schnipsel funktioniert, weil Sie die Definition der Funktion inline geschrieben haben, was bedeutet, dass der Compiler keinen Inline-Code für die Funktion generiert (zB, dass Sie Inlining deaktiviert haben oder der Compiler entscheidet Die Funktion ist zu groß, um inline zu sein. Der für die Funktionsdefinition generierte Code ist nur in der Übersetzungseinheit sichtbar, als ob Sie ihn in einem anonymen Namespace stecken hätten. Daher erhalten Sie mehrere Kopien der Funktion in dem generierten Objektcode, die der Linker möglicherweise optimiert, oder nicht, je nachdem wie intelligent er ist.

Sie könnten einen ähnlichen Effekt in Ihrem ersten Code-Snippet erzielen, indem Sie TestClass::testMethod() mit inline voranstellen.

-1
//Baseclass.h or .cpp 

#ifndef CDerivedclass 
#include "Derivedclass.h" 
#endif 

or 
//COthercls.h or .cpp 

#ifndef CCommonheadercls 
#include "Commonheadercls.h" 
#endif 

I think this suffice all instances. 
2

Eigentlich ist es möglich, Definitionen in einer einzigen Header-Datei (ohne separate .c/CPP-Datei) und noch in der Lage sein, es Dateien aus mehrere Quelle zu verwenden.

Betrachten Sie diese foobar.h Header:

#ifndef FOOBAR_H 
#define FOOBAR_H 

/* write declarations normally */ 
void foo(); 
void bar(); 

/* use conditional compilation to disable definitions when necessary */ 
#ifndef ONLY_DECLARATIONS 
void foo() { 
    /* your code goes here */ 
} 
void bar() { 
    /* your code goes here */ 
} 
#endif /* ONLY_DECLARATIONS */ 
#endif /* FOOBAR_H */ 

Wenn Sie nur eine Quelldatei in diesen Header verwenden, schließen und es in der Regel verwenden. Wie in main.c:

#include "foobar.h" 

int main(int argc, char *argv[]) { 
    foo(); 
} 

Wenn andere Quelldateien in Ihrem Projekt sind die foobar.h benötigen, dann #define ONLY_DECLARATIONS Makro, bevor es mit. In use_bar.c können Sie schreiben:

#define ONLY_DECLARATIONS 
#include "foobar.h" 

void use_bar() { 
    bar(); 
} 

Nach dem Übersetzen use_bar.o und main.o können ohne Fehler miteinander verbunden werden, denn nur einer von ihnen (main.o) Umsetzung der foo haben() und bar ().

Das ist etwas nicht idiomatisch, aber es erlaubt Definitionen und Deklarationen in einer Datei zusammen zu halten. Ich fühle mich wie ein Ersatz für einen armen Mann für echte modules.

Verwandte Themen