
\section{C++}\label{Cpp}
%
Die folgenden Abschnitte stellen zun"achst die f"ur die Entwicklung von
Bibliotheken wesentlichen Eigenschaften der Sprache C++ dar. Diese Abschnitte
der Arbeit sollen nat"urlich keine vollst"andige Einf"uhrung in die
Sprache\footnote{F"ur die Darstellung der Sprache verweist dieser Abschnitt auf
\cite{Strou92}. Weitere Darstellungen der Sprache finden sich unter
anderem auch in \cite{Strou94,Cop92,Lip89,Jos94,Amm95}.} sein.

Nach der Beschreibung der wichtigsten Eigenschaften der Sprache erfolgt eine
beispielhafte Darstellung einer C++-Bibliothek f"ur Massendaten. Dabei wird
untersucht, welche Eigenschaften der Sprache sich auf das Design der Bibliothek
ausgewirkt haben und wie dieser Einflu"s sich in der Bibliothek
niederschl"agt.

\subsection{Sprache}
C++ ist eine objektorientierte Programmiersprache, die Mehrfachvererbung
unterst"utzt. Eine Klasse, die von mehreren Oberklassen erbt, erh"alt alle
ihre Methoden und Variablen. Von dieser Regelung sind nur die mit {\sf
private} gekennzeichneten Teile der Oberklasse ausgenommen. Mit {\sf private}
gekennzeichnete Teile von Klassen sind weder f"ur Objekte einer abgeleiteten
Klasse noch f"ur andere Objekte zugreifbar.  


\subsubsection{Mehrfachvererbung}
Bei der Mehrfachvererbung kann es zu Mehrdeutigkeiten kommen. Eine
Mehrdeutigkeit liegt vor, wenn in beiden Oberklassen eine Methode mit
gleichem Namen vorhanden ist. F"ur einen solchen Fall gibt es in C++ keinen
eingebauten Mechanismus, um diese Mehrdeutigkeit aufzul"osen. Es mu"s
in der erbenden Klasse diese Methode neu definiert werden. Dies ist
ein Unterschied zu TooL. In TooL wird vom "Ubersetzer mit Hilfe eines
Algorithmus\footnote{Eine Beschreibung des Algorithmus befindet sich in
Abschnitt \ref{MehrfachVererbungAlgorithmus}} eine Methode ausgew"ahlt. Ein
m"oglicher Nachteil des in C++ gew"ahlten Verfahrens liegt in der m"oglichen
Codeverdopplung und Verminderung der Wiederverwendung, wenn eine der geerbten
Methoden auch in der erbenden Klasse h"atte verwendet werden k"onnen. Ein
Vorteil der in der Sprache C++ gew"ahlten Vorgehensweise zur Auswahl der zu
verwendenden Methode der Oberklassen liegt in der einfacheren
"Uberschaubarkeit und Steuerungsm"oglichkeit durch den Programmierer, da es
nicht n"otig ist, einen in die Sprache eingebauten Algorithmus zur Auswahl der
Methode zu ber"ucksichtigen.  

Da Vererbung eine Erweiterung der Konzepte einer Klasse und damit auch eine
Spezialisierung ihrer Eigenschaften bedeutet, mu"s der Vererbungsgraph auch in
C++ wie in jeder objektorientierten Sprache azyklisch und gerichtet sein
\cite[S. 201]{Strou92}. Zyklische Abh"angigkeiten zwischen Klassen lassen sich
nur mit Hilfe von {\sf friend} Klassen in einer Benutztbeziehung realisieren
\cite[S. 12]{Strou92}. 

\subsubsection{Kapselung}
Im Gegensatz zu anderen objektorientierten Programmiersprachen kennt C++ 3
Abstufungen der Zugriffskontrolle\footnote{Die im Rahmen dieser Arbeit
verwendete Sprache TooL kennt nur 2 Abstufungen.} auf Methoden und Variablen.
Die Steuerung der Zugriffskontrolle erfolgt durch die Einteilung der
Klassen in 3 Bereiche. Diese Bereiche werden durch die Schl"usselworte {\sf
public}, {\sf protected} und {\sf private} abgegrenzt. Ihre Bedeutung ist \cite[S. 47]{Strou92}: 
\begin{itemize}
\item {\sf \bf Public}. Die durch {\sf public} gekennzeichneten Variablen
und Methoden k"onnen von allen anderen Objekten aufgerufen werden.
Dieser Teil der Objekte einer Klasse unterliegt also keinerlei
Zugriffsbeschr"ankungen. 
\item {\sf \bf Protected}. Methoden und Variablen, die nur von Objekten
aus abgeleiteten Klassen und der Klasse selbst aufgerufen werden
d"urfen, kennzeichnet man in C++ als {\sf protected}. Methoden, die
{\sf protected} sind, entsprechen damit den {\sf private} Methoden in TooL. 
\item {\sf \bf Private}. Der als {\sf private} gekennzeichnete Teil einer
Klasse ist nur der Klasse selbst zug"anglich. Auch von dieser Klasse
abgeleitete Klassen haben keinen Zugriff. 
\end{itemize}

Eine solche Dreiteilung der Zugriffsberechtigungen gibt es in anderen
objektorientierten Programmiersprachen, wie z.B. TooL und Eiffel,
nicht. Um Verwirrungen zu vermeiden, hier eine tabellarische
Gegen"uberstellung:\newline
\begin{table}[hbt]
\begin{center} 
\leavevmode
\epsfxsize=14cm
\begin{tabular}{|l|l|l|}
\hline
Schl"usselwort & Bedeutung in C++ & Bedeutung in TooL\\ \hline \hline
{\sf private} & Nur die Objekte der definierenden & kein "Aquivalent
in\\ 
 & Klasse haben Zugriff & TooL vorhanden\\ \hline
{\sf protected} & Objekte aus definierender und & entspricht {\sf private} von TooL\\ 
& erbenden Klassen haben Zugriff & \\ \hline
{\sf public} & Objekte aller Klassen haben Zugriff & entspricht {\sf public}
von TooL\\ \hline
\end{tabular}\newline
\end{center}
\caption{Zugriffskontrolle in C++ und TooL}
\label{Zugriffskontrolle} 
\end{table}


Die Deklaration als {\sf private} bewirkt nicht nur eine Kapselung gegen"uber
benutzenden Klassen, sondern auch gegen"uber Erben. Ein Vorteil hiervon ist,
da"s die betreffenden Elemente nur innerhalb der deklarierenden Klasse benutzt
werden k"onnen und daher eine Ver"anderung der Semantik weder benutzende noch
erbende Klassen fehlerhaft werden l"a"st. Allerdings reduziert sich der Grad
der Wiederverwendung. 

Die durch die Deklaration als {\sf protected} oder {\sf private}
erreichte Kapselung der Objekte kann durchbrochen werden. Als
{\sf friend} deklarierte Methoden haben Zugriff auf alle privaten
Teile eines Objekts. Es ist auch m"oglich, alle "offentlichen Methoden einer
anderen Klasse als {\sf friend} einer Klasse zu definieren.Die Umgehung der
Schnittstelle des Objekts erm"oglicht in manchen F"allen eine erhebliche
Steigerung der Effizienz. In vielen F"allen f"uhren Methoden bestimmte Test,
beispielsweise von Bereichsgrenzen,  vor dem Zugriff auf die Variablen
durch. Der direkte Zugriff ohne Verwendung der Schnittstelle umgeht diese Tests
und ist daher schneller.

Dem Effizienzgewinn steht jedoch eine nicht unerhebliche Gefahr
des unkontrollierten Zugriffs gegen"uber. Auch m"u"ste in einem guten
Design erst gekl"art werden, ob nicht ein geeignetes Design der Bibliothek oder
des Systems die {\sf friend}-Deklaration vermeiden kann. 
Hier k"ame beispielsweise eine gemeinsame Oberklasse der durch {\sf friend}
verbundenen Klassen in Betracht, da diese ohnehin Zugriff auf die internen
Strukturen h"atte. Interessant ist, da"s in \cite[S. 13]{Strou92} empfohlen
wird, {\sf friend} m"oglichst zu vermeiden. 

In \cite[S. 34]{Strou92} wird noch der Begriff des Moduls eingef"uhrt. In der
Terminologie der Sprache C++ weicht die Bedeutung des Begriffs jedoch deutlich
von der Interpretation in Sprachen wie beispielsweise Modula ab. W"ahrend
Modula-2 durch Module Definition und Implementierung trennt, sind Module in
C++ nur Einheiten der "Ubersetzung. Von einer inhaltlichen Trennung oder einer
Kapselung ist nicht die Rede. Der Begriff Modul ist also in diesem Zusammenhang
eher irref"uhrend.  

\subsubsection{Typsystem}
%%
Wesentliche Eigenschaften des Typsystems von C++ sind:
\begin{itemize}
\item Statische Typisierung
\item Typumwandlung
\item "Uberladen von Methoden
\item Optionale Argumente:\newline 
      Die Angabe der obligatorischen Parameter ist ausreichend.
\item Polymorphismus
\end{itemize}

\vspace{3mm}
\noindent {\bf Statische Typisierung} \newline

\noindent C++ ist statisch typisiert \cite[S. 44]{Strou92}. Deshalb mu"s "uber
den Typ eines Objekts ein Minimum an Wissen zur "Ubersetzungszeit bereits
vorhanden sein. So ist beispielsweise auch kein allgemeiner {\sf Stack}
m"oglich, der Objekte beliebigen Typs als Element aufnehmen kann. In einem
solchen Stack w"are "uber die Elemente nicht bekannt, welche
Methoden sie verstehen. Eine Angabe des Elementtyps der Beh"alterklassen ist
daher unverzichtbar. C++ bietet jedoch die M"oglichkeit, eine 
Stack-Schablone ({\it template}) zu schreiben. Diese Schablone braucht den Typ
ihrer Elemente noch nicht zu spezifizieren. Der Typtest der Schablone findet
allerdings auch erst bei ihrer Instantiierung mit dem Elementtyp statt. Die
Schablone selbst kann also nicht wie eine Klasse verwendet werden, sondern ist
nur ein Hilfsmittel, mit dem Klassen erzeugt werden k"onnen. 

\vspace{3mm}
\noindent {\bf Typumwandlung}\newline

\noindent C++ kennt mehrere Formen der Typumwandlung ({\it type cast}). Eine
Form der Typumwandlung ver"andert lediglich den vom "Ubersetzer angenommenen
Typ eines Ausdrucks, w"ahrend bei einer zweiten Form zus"atzlich eine
Umwandlung der internen Repr"asentation des Objekts erfolgt. Dieser Abschnitt
beschreibt beide Formen der Typumwandlung. 

Im Rahmen der inkrementellen Erweiterung von Klassen einer objektorientierten
Bibliothek werden h"aufig Objekte im Kontext ihrer jeweiligen Oberklasse
verwendet. F"ur den Vorgang der "Ubersetzung bedeutet dies, da"s Objekte in
einem Kontext auftreten, in dem Objekte eines anderen Typs, n"amlich des
Supertyps, erwartet werden. Wenn der Subtyp im Kontext des Supertyps auftritt,
f"uhrt der "Ubersetzer bzw. Typtester eine Konvertierung des Objektes vom
Subtyp in den Supertyp durch. Diese Konvertierung bedeutet jedoch nicht, da"s
zur Laufzeit des Programmes die interne Repr"asentation der Objekte ver"andert
w"urde. Lediglich der vom "Ubersetzer zur "Ubersetzungszeit angenommene Typ des
Objekts wird ver"andert. Die interne Repr"asentation des Subtyps ist wegen der
Spezialisierung des Subtyps bereits f"ur die Operationen im Kontext des
Supertyps ausreichend. 

Eine typische Anwendung f"ur die Typumwandlung sind die Parameter und
R"uckgabetypen von Methoden. Diese Parameter sind in der Bibliothek von C++ in
der Regel vom Typ {\sf Object}. Ein Beispiel ist die Methode {\sf copy}.
Auch {\sf copy} ist so definiert, da"s es {\sf Object} liefert. Erbende
Klassen erwarten jedoch ein Objekt des jeweiligen Typs der Klasse selbst. Da
der Typ des Ergebnisses als {\sf Object} angegeben ist, k"onnen eigentlich nur
die Methoden der Klasse {\sf Object} auf das Ergebnis angewendet werden. Um die
Ergebnisse der jeweiligen Kopiermethode dennoch im normalen Kontext der Klasse
des kopierten Objekts verwenden zu k"onnen, werden die R"uckgabewerte vom
Compiler in den jeweiligen Typ umgewandelt. Die interne Repr"asentation bleibt
dabei unver"andert.

Typumwandlung mit gleichzeitiger Ver"anderung der internen Repr"asentation
hingegen ist beispielsweise durch explizite Angabe im Quelltext m"oglich  
\cite[S. 106f]{Strou92}. Zus"atzlich zur expliziten Typumwandlung erfolgt in
C++ eine Typumwandlung implizit durch den "Ubersetzer, wenn Werte verschiedenen
Typs miteinander verkn"upft werden sollen \cite[S. 40]{Strou92}.

\vspace{3mm}
\noindent {\bf "Uberladen von Methoden} \newline

\noindent Wenn der Name zweier Methoden identisch ist, sie jedoch Argumente 
unterschiedlichen Typs erwarten, spricht man von "uberladenen
Methoden ({\it overloading}) \cite[S. 142]{Strou92}. "Uberladene
Methoden dienen dazu, "ahnliche Aktionen auf Objekten verschiedenen Typs
auszuf"uhren. Um den Benutzer "Ubersicht zu geben, kann der Entwickler
der Bibliothek Methoden den gleichen Namen geben. Allerdings
erzwingt die Sprache C++ nicht, da"s die Methoden wirklich "ahnliche
Aktionen auf den Daten ausf"uhren. Die Kontrolle liegt hier also allein
beim Entwickler. Er kann auch v"ollig verschiedene Methoden mit Hilfe des
"Uberladens mit dem gleichen Namen versehen. Das "Uberladen braucht im
Quelltext nicht besonders deklariert zu werden. 
Der "Ubersetzer sucht abh"angig vom Typ der Parameter eines konkreten Aufrufs
die jeweils passende Funktionalit"at der "uberladenen Methode heraus. Im Fall
von Mehrdeutigkeiten weist er den Code zur"uck. 

\vspace{3mm}
\noindent {\bf Optionale Argumente} \label{OptArg} \newline

\noindent Es ist in C++ m"oglich \cite[S. 145]{Strou92}, bei der Deklaration
einer Methode nur die von ihr mindestens verlangten Parameter
anzugeben. Ein konkreter Aufruf einer solchen Methode kann jedoch eine
beliebige Zahl weiterer Parameter enthalten. Zum Zeitpunkt der
"Ubersetzung sind weder die Anzahl noch der Typ dieser Parameter
bekannt.  Zus"atzlich gibt es auch optionale Parameter, bei denen ein
Standardparameter ({\it default parameter}) angegeben ist.

\vspace{3mm}
\noindent {\bf Polymorphismus} \newline

\noindent Die Sprache C++ bietet drei M"oglichkeiten, Polymorphismus zu
realisieren: 
\begin{itemize}
\item[1. ] Mit Hilfe von (Typ-) Schablonen ({\it templates}).
\item[2. ] Durch Zeiger auf Funktionen.
\item[3. ] Inklusionspolymorphismus
\end{itemize}

\vspace{3mm}
\noindent {\bf Polymorphismus mit Hilfe von Schablonen}\newline

\label{Abstraktion und Polymorphismus}
\noindent Schablonen erm"oglichen die Abstraktion von Elementtypen, indem mit
Hilfe eines Typparameters ein neuer Typ erzeugt wird \cite[S. 12, S.
275ff]{Strou92}.  
Schablonen sind besonders bei der Realisierung von Beh"alterklassen von
Bedeutung. Beh"alterklassen sollen Funktionalit"at unabh"angig davon anbieten,
welchen Typ die in ihnen gespeicherten Objekte zur Laufzeit haben\footnote{Ein
Verzicht auf diese Forderung f"uhrt zu einer Vervielfachung der Beh"alter, 
z.B. {\sf IntegerStack}, {\sf RealStack} oder {\sf CharStack}.}. 

Da der einer Schablone "ubergebene Typ unbekannt ist, sind Annahmen "uber den
Typ und damit die F"ahigkeiten der Elemente des Beh"alters unm"oglich.
Es sind deshalb im Unterschied zur Sprache TooL, die eine Einschr"ankung des
Elementtyps auf eine Menge von Typen erlaubt, keine Mindestanforderungen
bekannt, welche die Elemente des Beh"alters erf"ullen. 

Allerdings birgt die in C++ gew"ahlte Regelung auch gro"se Vorteile.
Da der Zwang zur Angabe der zul"assigen Elementtypen entf"allt,
erlaubt C++, zun"achst die Funktionalit"at der Schablone zu implementieren,
ohne sich Gedanken "uber den Elementtyp zu machen. Erst bei der Instantiierung
einer Schablone wird vom "Ubersetzer gepr"uft, ob die Elemente des dann
angegebenen Typs den Anforderungen der Schablone gen"ugen.

Dieser Test des "Ubersetzers ist m"oglich, weil sich die Anforderungen der
Schablone an den zu "ubergebenden Typ aus dem Code der Schablone selbst
herleiten lassen. Der "Ubersetzer analysiert also den Code der Schablone und
erh"alt auf diese Weise eine Liste von Zugriffen auf die Objekte des
Elementtyps. Durch einen Vergleich dieser Anforderungsliste mit der
Schnittstelle der Objekte kann die Vertr"aglichkeit von Elementtyp und
Schablone zum Zeitpunkt der Instantiierung entschieden werden. Da f"ur die
Entscheidung der Typvertr"aglichkeit nur die tats"achlich an die Elemente
gestellten Anforderungen herangezogen werden, stellt der Typtest von C++
lediglich Mindestanforderungen an den Typ der Elemente der Schablone. 

\vspace{3mm}
\noindent {\bf Polymorphismus mit Hilfe von Zeigern auf Funktionen} \newline

\noindent Mit Hilfe von Zeigern auf Funktionen lassen sich polymorphe Routinen
realisieren \cite[S. 149]{Strou92}. Das dortige Beispiel offenbart jedoch, da"s
der Polymorphismus eigentlich nicht auf der F"ahigkeit der Sprache beruht,
Zeiger auf Funktionen zu verwenden. Vielmehr entsteht dieser Polymorphismus aus
der M"oglichkeit, Funktionen zu verwenden, bei denen der Typ der Parameter
nicht spezifiziert werden mu"s. Diese Art von Polymorphismus ist daher nicht
typsicher. Es handelt sich nicht um den in Abschnitt \ref{Polymorphismus}
beschriebenen parametrischen Polymorphismus, da dieser einen Typ als Parameter
verlangt, der die Typ"uberpr"ufung erm"oglicht. 

\vspace{3mm}
\noindent {\bf Inklusionspolymorphismus} \newline

\noindent Eine weitere in C++ angebotene Form von Polymorphismus ist der in
Abschnitt \ref{Polymorphismus} beschriebene Inklusionspolymorphismus. Dies
bedeutet, da"s Objekte auch im Kontext ihrer Oberklasse auftreten k"onnen.

\subsubsection{"Uberschreiben von Methoden}\label{UeberschreibenVonMethoden}
%%
Das "Uberschreiben von Methoden ist zul"assig, wenn sie bei ihrer
ersten Definition als virtuell ({\it virtual}) definiert wurden \cite[S.
204f]{Strou92}. Der Typ einer solchen virtuellen Methode darf bei der
Redefinition nicht ver\-"an\-dert werden. Allerdings ist es in C++ zul"assig,
beim "Uberschreiben die Methode in eine andere Kategorie einzuordnen. So ist es
beispielsweise erlaubt, durch "Uberschreiben einer "offentlichen Methode diese
in eine private Methode umzuwandeln. Durch diese Umwandlung ist die Methode
nicht mehr Bestandteil der Schnittstelle der erbenden Klasse und kann
deshalb dort nicht mehr direkt aufgerufen werden. Der Aufruf der Methode ist
nur noch nach vorheriger Umwandlung des entsprechenden Objekts in den Typ der
Oberklasse m"oglich. Diese M"oglichkeit ist ein wesentlicher Unterschied zur
Programmiersprache TooL. In TooL ist es nicht zul"assig, eine geerbte
Methode einer anderen Kategorie zuzuordnen. 

Bei einer unvorsichtigen Gestaltung der Bibliothek kommt es vor, da"s Methoden
geerbt werden, die in der erbenden Klasse nicht mehr implementierbar sind, da
sie den Eigenschaften der erbenden Klasse widersprechen. Bei der Verwendung von
C++ kann in einer solchen Situation eine Ver"anderung des Entwurfs der
Bibliothek vermieden werden, indem die nicht mehr "`passende"' Methode aus der 
Schnittstelle entfernt wird. Das Entfernen von Methoden aus der Schnittstelle
stellt einen Versto"s gegen das Prinzip der inkrementellen Erweiterung und
Spezialisierung dar und wird daher von TooL nicht unterst"utzt.





