This article, published in a German PC magazine called ‘DOS International’, was written during a summer C++ course at Saarland University.

DOS International

Initially, it was intended as a submission for a contest to write a useful program in just 1024 bytes. The prize was 1024 Deutsche Mark, but unfortunately the program was considered too specialized to be generally useful. However, the contribution was later reused in the magazine’s programmer’s corner: ’tips and tricks for programmers’.

Below is the German version of the article as published in ‘DOS International’ in September 1993.

Vektortemplate

vektor.cpp (Listing 2) kompilieren Sie mit Borland C++ ab Version 3.1.

Ein Vektortemplate nutzen alle begeisterten C++-Anhänger, die wiederverwendbare Software schreiben. Es kann als Ausgangspunkt einer benutzerdefinierten, mathematischen Bibliothek dienen und bietet dem Anwender viele Wege zur Erweiterung, wie zum Beispiel ein Matrixtemplate, das aus mehreren Vektoren dargestellt wird. Das Ziel der Vektorrepräsentation durch ein Template ist es, unter Ausnutzung der Überladungsmöglichkeiten von Operatoren und Funktionen (operator-overloading, function-overloading) eine Syntax zu gestatten, wie es in der Mathematik üblich ist. Damit erreichen Sie besser lesbaren und damit leichter verständlichen Code. Der zweite Vorteil des Templates ist, daß es dem Anwender überlassen ist, welchen Datentyp er in der Instanz seines Vektors speichern möchte. Durch die beiden Konstruktoren und das Default-Argument ergeben sich drei verschiedene Arten der Definition eines Vektorobjektes vom Typ v.

Die erste Definition erzeugt einen dreidimensionalen Vektor v1 mit Komponenten vom Typ float.

v<float> v2 = v1;

Die zweite Definition erzeugt ein komplettes Duplikat des Vektors v1 mit eigenem Komponentenspeicher.

v<float> v3(4);

Die dritte Definition erzeugt den vierdimensionalen Vektor v3 unter expliziter Angabe der Dimension.Die Elementfunktion 4 liefert als Rückgabewert die Dimension eines Vektors:

cout<<v1.d()<<"-dimensional"; //Ausgabe:3-dimensional

Durch das Überladen des Subskriptoperators [] kann der Anwender explizit auf die einzelnen Komponenten eines Vektors zugreifen:

cout << v1[1]; //Ausgabe:0,weil erste Komponente hier noch
               // nicht initialisiert ist.

Die Subskriptfunktion liefert als Rückgabe eine Referenz auf die spezifizierte Komponente. Dadurch sind Sie in der Lage, mit dem Subskriptoperator eine Initialisierung des Vektors durchzuführen.

v1[1]=2.0;
v1[2]=3.5;
v1[3]=1.7;
v2[1]=5.7;
v2[2]=9.1;
v2[3]=4.8;

Mit der Nummerierung hält sich die Operatorfunktion an die mathematische Notation. Der Anwender spricht bei einem dreidimensionalen Vektor die Komponenten mit den Indizes 1 bis 3 an. Template-intern wird zwangsläufig auf die normale C++-Indizierung von 0 bis 2 umgerechnet. Das Überladen des C++-Ausgabeoperators << erlaubt die Ausgabe beliebig-dimensionaler Vektoren innerhalb der gewohnten Ausgabesequenzen;

cout << "v1=" << v1 << '\n'; // Ausgabe:v1=[2.0,3.5,1.7]

Die arithmetischen Operatoren +, -, * und der Zuweisungsoperator = wurden überladen, um die einfachen mathematischen Vektoroperationen zu gestatten: v3=v1;. Der Zuweisungsoperator kopiert die Komponenten und die Dimension von bestehenden Objekten. Im Unterschied zum Copy-Konstruktor wird kein neues Objekt erzeugt, sondern ein bestehendes überschrieben. Die auf Vektoren definierte Skalarmultiplikation unterliegt jedoch einigen Beschränkungen. Der linke Operand muß ein lvalue sein, und es können nur Elemente gleichen Datentyps miteinander multipliziert werden oder Elemente, die sich nach den Typumwandlungsregeln von C++ in den Datentyp der Komponenten verwandeln lassen.

cout << v1*3; //Ausgabe:[6.0,10.5,5.1]

Der Additionsoperator stellt die komponentenweise Addition zweier Vektoren zur Verfügung.

cout << vl+v2; //Ausgabe:[7.7,12.6,6.5]

Um bei der Vektorsubtraktion nicht den identischen Code des Additionsoperators zu benutzen, nur mit dem Unterschied eines einzigen Zeichens, wurde die Subtraktion mit Hilfe der Addition und Skalarmultiplikation implementiert. Die Anweisung return *this + w * -1; führt mit jeder Komponente des Übergabeparameters w eine Multiplikation mit -1 durch und addiert danach die beiden Vektoren. Der Subtraktionsoperator kann dann beispielsweise so genutzt werden:

cout << vl-v2; //Ausgabe:[-3.7,-5.6,-3.1]

Dieses Beispiel trägt hoffentlich dazu bei, die Stärken von C++ gegenüber prozeduralen Programmiersprachen zu verdeutlichen und herauszustellen, wie einfach es ist, wiederverwendbaren Code zu schreiben. Das Template soll dem Benutzer als Anregung dienen, sich mit den Stärken von C++ auseinanderzusetzen und nach Erweiterung der Funktionen soll es sich als nützlich im Zusammenhang mit der Entwicklung mathematischer Software erweisen.

(Michael Rumpf/et)

1: //Compiler: Borland C++ 3.1
2: #include <iostream.h>
3: template<class T>class v{
4:  int z;
5:  T* a;
6: public:
7:  v(intd=3){a=new T[z=d];}
8:  v(v<T>&);
9:  -v()<delete a;}
10:  int d()(return z;);
11:  T&operator()(int i);
12:  v<T>operator+(v<T>);
13:  v<T>operator*lT);
14:  v<T>operator-(v<T>);
15:  v<T>operator=(v<T>);
16: friend ostream&operator<<
17:  (ostream&os,v<T>w););
18: template<class T>ostream
19:  &operator<< (ostream&os,v<T>w)
20: {os<<" ["; for (int i=1; i<w.z;i++)
21: os<<w[i]<<',';
22: return os<<w [w.z] <<"] ": }
23: template<class T>v<T>::v(v<T>&w)
24: {a=new T[z=w.z];
25: for(int i=0;i<z;ii+) a[i]=w.a[i];}
26: template<
27:   class T>T&v<T>::operatori[](int i)
28:{if(i>=1&&i<=z)return a[i-1];}
29: template<
30:   class T>v<T>v<T>::operator+(v<T>w)
31: {v<T>s(2);
32: if(z==w.z)for(int i=0;
33: i<z;i++)s.a[i]*=(a+i)+w.a[i];
34: return s;}
35: template<
36:   class T>v<T>v<T>::operator*(T s)
37: {v<T>w(z);
38: for(int i=O;i<z;
39: i++)w.a[i]=a[i]*s;return w;}
40: template<
41:   class T>v<T>v<T>::operator-(v<T>w)
42: {return *this+(w*-1),}
43: template<
44:   class T>v<T>v<T>::operator=(v<T>w)
45: {delete a;a=new T[z=w.z];
46: for(int i=0;
47: i<z;i++)a[i]=w.a[i];return *this;)
48: void main()(v<int> vl,v2;
49:vi[1]=4;vi[2]=6;vi[3]=8;
50: v2=vl;cout (r) vl (r) vl + v2;
51: cout (r) '\n'(r) vi*3; }

vektor.cpp< beweist, was C++-Programmierer besser als die Kollegen in prozeduralen Programmiersprachen können.

TypeArticle for a Computer Magazine
EnvironmentSun Solaris 5, Windows 3.x, Borland C++ 3.1, GCC