Freigeben über


Klassenvorlagen

In diesem Artikel werden Regeln beschrieben, die spezifisch für C++-Klassenvorlagen sind.

Memberfunktionen von Klassenvorlagen

Memberfunktionen können innerhalb oder außerhalb einer Klassenvorlage definiert sein. Sie werden wie Funktionsvorlagen definiert, wenn sie außerhalb der Klassenvorlage definiert sind.

// member_function_templates1.cpp
template<class T, int i> class MyStack
{
    T*  pStack;
    T StackBuffer[i];
    static const int cItems = i * sizeof(T);
public:
    MyStack( void );
    void push( const T item );
    T& pop( void );
};

template< class T, int i > MyStack< T, i >::MyStack( void )
{
};

template< class T, int i > void MyStack< T, i >::push( const T item )
{
};

template< class T, int i > T& MyStack< T, i >::pop( void )
{
};

int main()
{
}

Wie bei jeder Elementfunktion der Vorlagenklasse enthält die Definition der Konstruktorelementfunktion der Klasse zweimal die Vorlagenargumentliste.

Memberfunktionen können selbst Funktionsvorlagen sein und zusätzliche Parameter angeben, wie im folgenden Beispiel gezeigt.

// member_templates.cpp
template<typename T>
class X
{
public:
   template<typename U>
   void mf(const U &u);
};

template<typename T> template <typename U>
void X<T>::mf(const U &u)
{
}

int main()
{
}

Geschachtelte Klassenvorlagen

Vorlagen können innerhalb von Klassen oder Klassenvorlagen definiert werden, in diesem Fall werden sie als Membervorlagen bezeichnet. Membervorlagen, die Klassen sind, werden als geschachtelte Klassenvorlagen bezeichnet. Membervorlagen, die Funktionen sind, werden in Member-Funktionsvorlagen erläutert.

Geschachtelte Klassenvorlagen werden als Klassenvorlagen innerhalb des Bereichs der äußeren Klasse deklariert. Sie können innerhalb oder außerhalb der einschließenden Klasse definiert sein.

Das folgende Codebeispiel zeigt eine verschachtelte Klassenvorlage innerhalb einer normalen Klasse.

// nested_class_template1.cpp
// compile with: /EHsc
#include <iostream>
using namespace std;

class X
{
   template <class T>
   struct Y
   {
      T m_t;
      Y(T t): m_t(t) { }
   };

   Y<int> yInt;
   Y<char> yChar;

public:
   X(int i, char c) : yInt(i), yChar(c) { }
   void print()
   {
      cout << yInt.m_t << " " << yChar.m_t << endl;
   }
};

int main()
{
   X x(1, 'a');
   x.print();
}

Der folgende Code verwendet geschachtelte Vorlagentypparameter, um geschachtelte Klassenvorlagen zu erstellen:

// nested_class_template2.cpp
// compile with: /EHsc
#include <iostream>
using namespace std;

template <class T>
class X
{
   template <class U> class Y
   {
      U* u;
   public:
      Y();
      U& Value();
      void print();
      ~Y();
   };

   Y<int> y;
public:
   X(T t) { y.Value() = t; }
   void print() { y.print(); }
};

template <class T>
template <class U>
X<T>::Y<U>::Y()
{
   cout << "X<T>::Y<U>::Y()" << endl;
   u = new U();
}

template <class T>
template <class U>
U& X<T>::Y<U>::Value()
{
   return *u;
}

template <class T>
template <class U>
void X<T>::Y<U>::print()
{
   cout << this->Value() << endl;
}

template <class T>
template <class U>
X<T>::Y<U>::~Y()
{
   cout << "X<T>::Y<U>::~Y()" << endl;
   delete u;
}

int main()
{
   X<int>* xi = new X<int>(10);
   X<char>* xc = new X<char>('c');
   xi->print();
   xc->print();
   delete xi;
   delete xc;
}

/* Output:
X<T>::Y<U>::Y()
X<T>::Y<U>::Y()
10
99
X<T>::Y<U>::~Y()
X<T>::Y<U>::~Y()
*/

Lokale Klassen dürfen keine Membervorlagen haben.

Vorlagenfreunde

Kursvorlagen können Freunde haben. Eine Klasse oder Klassenvorlage, Funktion oder Funktionsvorlage kann ein Friend einer Vorlagenklasse sein. Friends können auch Spezialisierungen einer Klassenvorlage oder Funktionsvorlage sein, jedoch keine teilweisen Spezialisierungen.

Im folgenden Beispiel wird eine Friend-Funktion als Funktionsvorlage innerhalb der Klassenvorlage definiert. Dieser Code erstellt eine Version der Friend-Funktion für jede Instanziierung der Vorlage. Dieses Konstrukt ist nützlich, wenn die Friend-Funktion von denselben Vorlagenparametern wie die Klasse abhängt.

// template_friend1.cpp
// compile with: /EHsc

#include <iostream>
using namespace std;

template <class T> class Array {
   T* array;
   int size;

public:
   Array(int sz): size(sz) {
      array = new T[size];
      memset(array, 0, size * sizeof(T));
   }

   Array(const Array& a) {
      size = a.size;
      array = new T[size];
      memcpy_s(array, a.array, sizeof(T));
   }

   T& operator[](int i) {
      return *(array + i);
   }

   int Length() { return size; }

   void print() {
      for (int i = 0; i < size; i++)
         cout << *(array + i) << " ";

      cout << endl;
   }

   template<class T>
   friend Array<T>* combine(Array<T>& a1, Array<T>& a2);
};

template<class T>
Array<T>* combine(Array<T>& a1, Array<T>& a2) {
   Array<T>* a = new Array<T>(a1.size + a2.size);
   for (int i = 0; i < a1.size; i++)
      (*a)[i] = *(a1.array + i);

   for (int i = 0; i < a2.size; i++)
      (*a)[i + a1.size] = *(a2.array + i);

   return a;
}

int main() {
   Array<char> alpha1(26);
   for (int i = 0 ; i < alpha1.Length() ; i++)
      alpha1[i] = 'A' + i;

   alpha1.print();

   Array<char> alpha2(26);
   for (int i = 0 ; i < alpha2.Length() ; i++)
      alpha2[i] = 'a' + i;

   alpha2.print();
   Array<char>*alpha3 = combine(alpha1, alpha2);
   alpha3->print();
   delete alpha3;
}
/* Output:
A B C D E F G H I J K L M N O P Q R S T U V W X Y Z
a b c d e f g h i j k l m n o p q r s t u v w x y z
A B C D E F G H I J K L M N O P Q R S T U V W X Y Z a b c d e f g h i j k l m n o p q r s t u v w x y z
*/

Das folgenden Beispiel befasst sich mit einem Friend mit Vorlagenspezialisierung. Eine Funktionsvorlagenspezialisierung ist automatisch ein Friend, wenn die ursprüngliche Funktionsvorlage ein Friend ist.

Es ist auch möglich, nur die spezialisierte Version der Vorlage als Freund zu deklarieren, wie der Kommentar vor der Deklaration des Freundes im folgenden Code angibt. Wenn Sie eine Spezialisierung als Freund deklarieren, müssen Sie die Definition der Spezialisierung der Freundvorlage außerhalb der Vorlagenklasse platzieren.

// template_friend2.cpp
// compile with: /EHsc
#include <iostream>
using namespace std;

template <class T>
class Array;

template <class T>
void f(Array<T>& a);

template <class T> class Array
{
    T* array;
    int size;

public:
    Array(int sz): size(sz)
    {
        array = new T[size];
        memset(array, 0, size * sizeof(T));
    }
    Array(const Array& a)
    {
        size = a.size;
        array = new T[size];
        memcpy_s(array, a.array, sizeof(T));
    }
    T& operator[](int i)
    {
        return *(array + i);
    }
    int Length()
    {
        return size;
    }
    void print()
    {
        for (int i = 0; i < size; i++)
        {
            cout << *(array + i) << " ";
        }
        cout << endl;
    }
    // If you replace the friend declaration with the int-specific
    // version, only the int specialization will be a friend.
    // The code in the generic f will fail
    // with C2248: 'Array<T>::size' :
    // cannot access private member declared in class 'Array<T>'.
    //friend void f<int>(Array<int>& a);

    friend void f<>(Array<T>& a);
};

// f function template, friend of Array<T>
template <class T>
void f(Array<T>& a)
{
    cout << a.size << " generic" << endl;
}

// Specialization of f for int arrays
// will be a friend because the template f is a friend.
template<> void f(Array<int>& a)
{
    cout << a.size << " int" << endl;
}

int main()
{
    Array<char> ac(10);
    f(ac);

    Array<int> a(10);
    f(a);
}
/* Output:
10 generic
10 int
*/

Das folgende Beispiel zeigt eine Friend-Klassenvorlage, die innerhalb einer Klassenvorlage wurde. Die Klassenvorlage wird dann als Vorlagenargument für die Friend-Klasse verwendet. Friend-Klassenvorlagen müssen außerhalb der Klassenvorlage definiert werden, in der sie deklariert werden. Alle Spezialisierungen oder Teilspezialisierungen der Friend-Vorlage sind auch Friends der ursprünglichen Klassenvorlage.

// template_friend3.cpp
// compile with: /EHsc
#include <iostream>
using namespace std;

template <class T>
class X
{
private:
   T* data;
   void InitData(int seed) { data = new T(seed); }
public:
   void print() { cout << *data << endl; }
   template <class U> friend class Factory;
};

template <class U>
class Factory
{
public:
   U* GetNewObject(int seed)
   {
      U* pu = new U;
      pu->InitData(seed);
      return pu;
   }
};

int main()
{
   Factory< X<int> > XintFactory;
   X<int>* x1 = XintFactory.GetNewObject(65);
   X<int>* x2 = XintFactory.GetNewObject(97);

   Factory< X<char> > XcharFactory;
   X<char>* x3 = XcharFactory.GetNewObject(65);
   X<char>* x4 = XcharFactory.GetNewObject(97);
   x1->print();
   x2->print();
   x3->print();
   x4->print();
}
/* Output:
65
97
A
a
*/

Wiederverwenden von Vorlagenparametern

Vorlagenparameter können in der Vorlagenparameterliste wiederverwendet werden. Es ist beispielsweise der folgende Code zulässig:

// template_specifications2.cpp

class Y
{
};
template<class T, T* pT> class X1
{
};
template<class T1, class T2 = T1> class X2
{
};

Y aY;

X1<Y, &aY> x1;
X2<int> x2;

int main()
{
}

Siehe auch

Vorlagen