Hinweis
Für den Zugriff auf diese Seite ist eine Autorisierung erforderlich. Sie können versuchen, sich anzumelden oder das Verzeichnis zu wechseln.
Für den Zugriff auf diese Seite ist eine Autorisierung erforderlich. Sie können versuchen, das Verzeichnis zu wechseln.
Wenn Sie eine Klassenvorlage definieren, müssen Sie den Quellcode so organisieren, dass die Memberdefinitionen für den Compiler sichtbar sind, wenn dieser sie benötigt. Sie können zwischen einem Einschlussmodell und einem Modell der expliziten Instanziierung wählen. Im Einschlussmodell schließen Sie die Memberdefinitionen in jede Datei ein, die eine Vorlage verwendet. Diese Vorgehensweise ist die einfachsten und bietet maximale Flexibilität im Hinblick auf die konkreten Typen, die mit Ihrer Vorlage verwendet werden können. Der Nachteil ist, dass sich die Kompilierungszeiten verlängern können. Die Zeiten können erheblich sein, wenn ein Projekt oder die enthaltenen Dateien selbst groß sind. Beim Ansatz der expliziten Instanziierung instanziiert die Vorlage selbst konkrete Klassen oder Klassenmember für bestimmte Typen. Dies kann die Kompilierung beschleunigen, beschränkt aber die Verwendung auf diejenigen Klassen, die bei der Vorlagenimplementierung im Voraus aktiviert wurden. Im Allgemeinen empfehlen wir das Einschlussmodell, es sei denn, dass die Kompilierungszeiten ein Problem darstellen.
Hintergrund
Vorlagen sind nicht wie normale Klassen im Sinne, dass der Compiler keinen Objektcode für eine Vorlage oder eines seiner Member generiert. Es gibt nichts zu generieren, bis die Vorlage mit konkreten Typen instanziiert wird. Wenn der Compiler eine Vorlageninstanziierung wie MyClass<int> mc;
findet, aber noch keine Klasse mit dieser Signatur vorhanden ist, generiert der Compiler eine neue Klasse. Er versucht auch, Code für alle verwendeten Memberfunktionen zu generieren. Wenn sich diese Definitionen in einer Datei befinden, die nicht direkt oder indirekt in der kompilierten .cpp Datei #included ist, kann der Compiler sie nicht sehen. Aus der Sicht des Compilers ist dies nicht unbedingt ein Fehler. Die Funktionen können in einer anderen Übersetzungseinheit definiert werden, in der sie vom Linker gefunden werden. Wenn der Linker diesen Code nicht findet, löst er einen nicht aufgelösten externen Fehler aus.
Das Einschlussmodell
Die einfachste und gängigste Art und Weise, Vorlagendefinitionen in einer Übersetzungseinheit sichtbar zu machen, besteht darin, die Definitionen in der Headerdatei selbst zu platzieren. Jede .cpp
Datei, die die Vorlage verwendet, muss einfach die #include
Kopfzeile aufweisen. Dieser Ansatz wird in der Standardbibliothek verwendet.
#ifndef MYARRAY
#define MYARRAY
#include <iostream>
template<typename T, size_t N>
class MyArray
{
T arr[N];
public:
// Full definitions:
MyArray(){}
void Print()
{
for (const auto v : arr)
{
std::cout << v << " , ";
}
}
T& operator[](int i)
{
return arr[i];
}
};
#endif
Bei diesem Verfahren hat der Compiler Zugriff auf die vollständige Vorlagendefinition und kann Vorlagen nach Bedarf für jeden Typ instanziieren. Es ist einfach und relativ einfach zu verwalten. Das Einschlussmodell weist jedoch in Sachen Kompilierungszeit einen Nachteil auf. Dieser Nachteil kann in großen Programmen sehr schwer wiegen, insbesondere dann, wenn der Vorlagenheader selbst andere Header per „#include“ einschließt. Jede .cpp
Datei, die den Header #includes, erhält eine eigene Kopie der Funktionsvorlagen und aller Definitionen. Der Linker kann im Allgemeinen Dinge sortieren, sodass Sie nicht mit mehreren Definitionen für eine Funktion enden, aber es dauert Zeit, um diese Arbeit zu erledigen. In kleineren Programmen ist diese zusätzliche Kompilierungszeit wahrscheinlich nicht relevant.
Das Modell der expliziten Instanziierung
Wenn das Einschlussmodell für Ihr Projekt nicht geeignet ist und Sie definitiv den Satz von Typen kennen, die zum Instanziieren einer Vorlage verwendet werden, können Sie den Vorlagencode in eine .h
Datei .cpp
und in der .cpp
Datei trennen und die Vorlagen explizit instanziieren. Dieser Ansatz generiert Objektcode, den der Compiler sehen wird, wenn er auf Benutzerinstanziationen stößt.
Sie erstellen eine explizite Instanziierung mithilfe des Schlüsselworts template
gefolgt von der Signatur der Entität, die Sie instanziieren möchten. Diese Entität kann ein Typ oder ein Element sein. Wenn Sie explizit einen Typ instanziieren, werden alle Member instanziiert.
Die Headerdatei MyArray.h
deklariert die Vorlagenklasse MyArray
:
//MyArray.h
#ifndef MYARRAY
#define MYARRAY
template<typename T, size_t N>
class MyArray
{
T arr[N];
public:
MyArray();
void Print();
T& operator[](int i);
};
#endif
Die Quelldatei MyArray.cpp
instanziiert template MyArray<double, 5>
explizit und template MyArray<string, 5>
:
//MyArray.cpp
#include <iostream>
#include "MyArray.h"
using namespace std;
template<typename T, size_t N>
MyArray<T,N>::MyArray(){}
template<typename T, size_t N>
void MyArray<T,N>::Print()
{
for (const auto v : arr)
{
cout << v << "'";
}
cout << endl;
}
template MyArray<double, 5>;
template MyArray<string, 5>;
Im vorherigen Beispiel befinden sich die expliziten Instanziierungen am Ende der .cpp
Datei. A MyArray
kann nur für double
oder String
Typen verwendet werden.
Hinweis
In C++11 wurde das export
Schlüsselwort im Kontext von Vorlagendefinitionen veraltet. In der Praxis hat dies wenig Auswirkungen, da die meisten Compiler dieses Schlüsselwort nie unterstützt haben.