====== Interfaces ====== Interfaces nennt man Klassen, die ausschließlich aus virtuellen Funktionen bestehen und zwar ohne, dass es eine Implementierung gibt. Das bedeutet, dass diese Klasse soweit überhaupt nicht benutzbar ist. ===== pure virtual Methods ===== Methoden, die nicht implementiert werden, werden in C++ 'pure virtual' genannt. Andere Sprachen benutzen ein eigenes Schlüsselwort, häufig 'abstract', entsprechend spricht man auch von abstrakten Methoden. Eine 'pure virtual' Methode wird fast wie eine virtuelle Methode erstellt, sie wird lediglich zusätzlich mit 0 initialisiert: class Encoder { virtual bool Encode( char const * source, char * destination, int length ) = 0; virtual bool Decode( char const * source, char * destination, int length ) = 0; }; Die Klasse ''Encoder'' kann nun nicht mehr instanziiert werden, schließlich könnte man sonst die Funktion ''Encode'' nicht rufen, da sie bisher nicht implementiert wurde. Es gibt bisher ja nur die Behauptung, dass es eine Methode geben soll. Eine pure virtual Method muss also in einer Ableitung überschrieben werden. ===== Überschreiben von pure virtual Methods ===== Eine Klasse ''Encoder'' lässt sich nicht erstellen, bevor die beiden pure virtual Methods implementiert haben. Eine Ableitung muss also die Methoden implementieren oder bleibt eine abstrakte Klasse, die ebenfalls nicht instanziierbar ist. Das ist vollkommen in Ordnung, so lassen sich für eine weitere abstrakte Basisklasse weitere Methoden hinzufügen - abstrakte, wie auch implementierte. Nehmen wir nun an, dass wir nun eine Klasse implementieren wollen, die das Interface ''Encoder'' implementieren soll: class CaesarChiffre : public Encoder { bool Encode( char const * source, char * destination, int length ); bool Decode( char const * source, char * destination, int length ); }; bool CaesarChiffre::Encode( char const * source, char * destination, int length ) { int i; for( i = 0; i < length && source[ i ]; i++ ) { destination[i] = source[i]; if( source[i] >= 'A' && source[i] <= 'Z' ) if( destination[i] > 'C' ) destination[i] -=3; else destination[i] +=23; else if( source[i] >= 'a' && source[i] <= 'z' ) if( destination[i] > 'c' ) destination[i] -=3; else destination[i] +=23; } if( i < length ) destination[i++] = '\0'; return i <= length; } bool CaesarChiffre::Decode( char const * source, char * destination, int length ) { int i; for( i = 0; i < length && source[ i ]; i++ ) { destination[i] = source[i]; if( source[i] >= 'A' && source[i] <= 'Z' ) if( destination[i] < 'X' ) destination[i] +=3; else destination[i] -=23; else if( source[i] >= 'a' && source[i] <= 'z' ) if( destination[i] < 'x' ) destination[i] +=3; else destination[i] -=23; } if( i < length ) destination[i++] = '\0'; return i <= length; } ===== Die Bedeutung des Interface ===== Wir haben nun eine Schnittstellenbeschreibung (Interface) namens ''Encoder'', die Objekte kennzeichnet, wie man Texte kodiert und wieder dekodiert. Ein solches Objekt verfügt auf jeden Fall über die Methoden ''Encode'' und ''Decode''. Das Interface wird in der Klasse ''CaesarChiffre'' implementiert. Das Interface selbst besitzt keine Implementation und keine Variablen. Der Unterschied zur normalen Ableitung mit virtuellen Methoden liegt lediglich darin, dass ein Interface ein Sonderfall ist: es besitzt überhaupt keine Implementation. Warum wird es dann besonders behandelt? Klassen, die keine Variablen als Member haben, können auch nicht das [[cpp:inheritance:diamond|Diamant-Problem]] entwickeln. Sprachen wie C# oder Java bauen darauf, dass sie so das [[cpp:inheritance:diamond|Diamant-Problem]] ausklammern können. Wichtig dabei ist, dass Interfaces keine Variablen untergeschoben bekommen, die mit Hilfe einer Referenz auf eine Variable, die durch eine Get...-Methode zu erreichen ist. Das ist verlockend, um einem Objekt eine Konfiguration unterzujubeln, die dann über das Interface erreichbar wäre. Implementiert man ein solches Interface mehrfach, so gibt es auch mehrere Konfigurationen und schon haben wir mit dem Interface wieder die gleichen Probleme wie bei der Mehrfachvererbung: das [[cpp:inheritance:diamond|Diamant-Problem]]. Für ein Interface gilt es also darauf zu achten, dass nur Funktionen verwendet werden, die vollständig über die Parameter konfiguriert werden und die ihre Konfiguration nicht selbst halten. Müssen Objekte Daten halten, so handelt es sich um [[cpp:inheritance:multiple|Mehrfachvererbung]].