P
Noch eine kleine Zusatzerklärung:
Sauberes C++ ist in den meisten Fällen kein 100%ig sauberer OOCode.
Der Grund:
OOP hat einen grundsatz, genannt "Kapselung" bzw. "iformation hiding".
Das beruht darauf, dass der Benutzer eines Objektes/einer Klasse zwar wissen sollte, was die Klasse alles kann, aber nicht, wie sie es macht. Die Schnittstelle der Klasse ist im streng objektorientierten Fall also nichts weiter als alles, was in C++-Headern als "public" deklariert wird. Alles weitere sind Implementationsdetails, die den Klienten nichts angehen sollten.
Wo C++ dieses Prinzip verletzt: bei den oben erwähnten inline-methoden, die direkt im header angegeben werden, und bei allem, was private deklariert ist. Der Klient braucht schließlich nicht zu wissen, ob ich meine Werte intern in einem Struct oder einem Vector oder einem Array abspeichere, sondern nur, wie er auf die Werte zugreifen kann.
Eine weitestmöglich objektorientierte Ausführung einer Klasse bestünde aus einem header a lá
header:
class DasDing
{
private:
class DasDingImp; //Forward-deklaration für eine Implementationsklasse
DasDingImp * _myImp //Zeiger auf ein Implementationsobjekt
public:
DasDing(int rhs); //oder wie auch immer die Konstruktoren aussehen sollen
~DasDing();
zugriff1();
zugriff2(); //usw.
};
Abgesehen davon, dass es eine Klasse gibt, die das alles implementiert, wird dem Klienten rein garnichts verraten...
cpp-Datei:
#include dasding.hpp
//Definition der Implementationsklasse:
//hier kann mit inlines usw. gearbeitet werden
class DasDingImp
{
private:
int _einWert;
const string _noch_ein_Wert;
public:
DasDingImp(int rhs) : _einWert(rhs), _noch_ein_Wert("?") {};
~DasDingImp() {};
zugriff1(); //alle Methoden, die in der eigentlichen Klasse
zugriff2(); //vorkommen, haben hier ein pendant
priavte:
eine_kleine_hilfsmethode();
}
//Implementationen von DasDingImp's Methoden
//...
//Implementationen von DasDing-Methoden
//Konstruktoren alloziieren Speicher für den Implementationszeiger und rufen
//den konstruktor der Implementationsklasse auf
//Destruktor ruft delete für den Zeiger auf
//alle anderen Methoden werden stumpf weitergereicht
inline DasDing::DasDing(int rhs)
: _myImp(new DasDingimp(rhs)) {}
inline DasDing::~DasDing() { delete _myImp; }
inline zugriff1() { return _myImp->zugriff1(); }
inline zugriff2() { return _myImp->zugriff2(); }
//usw
natürlich könnte man der Übersicht halber die cpp-Datei in 2 oder drei dateien aufsplitten, worauf es aber am Ende ankommt ist, dass der Klient nur die Schnittstelle einbindet. Dazu kommt, dass, wenn ich die interne Darstellung der Klasse ändere (z.B. statt des int plötzlich ein long verwenden will oder eine selbstgeschriebene numerische Klasse), ich nicht mein ganzes Projekt neu kompilieren muss, sondern nur dasding-cpp, weil sich die hpp ja nicht ändert.
Wem das nach zu viel Arbeit ausschaut, dem sei gesagt, dass sich recht schnell ein perlscript (beispielsweise) schreiben lässt, dass aus der dasding.hpp ein Grundgerüst für die cpp baut mit sämtlichen öffentlichen Methodendeklarationen für die implementationsklasse und der "Implementierung" der Klasse "DasDing", die ja nur aus inline und weiterreichen besteht. So bleibt am Ende nur das übrig, was man so oder so noch tun muss: die privaten und geschützten Methoden und Datenmember deklarieren und die Methoden implementieren, nur eben nicht in der eigentlichen klasse sondern in der Implementationsklasse.
Man kann das Ganze noch weiter treiben: man kann die Konstruktoren von "DasDing" mit einem (evtl. optionalen) zusätzlichen Parameter versehen, in dem ein Zeiger auf ein Implementationsobjekt übergeben wird oder noch besser, auf eine Fabrikmethode, die so ein implementationsobjekt erzeugt. Schließlich haben wir die Polymorphie, und DasDingImp kann eine abstrakte Basisklasse sein, die verschiedene abgeleitete Implementationsklassen besitzt. So kann man zur Laufzeit auswählen, welche Implementation man denn gerne für das Objekt hätte. Das fängt an bei Implementationen, die geschwindigkeitsoptimiert sind, im vergleich zu Implementationen, die speicherplatzoptimiert sind, bis hi zu Implementationen, die völlig verschiedene Verhalten an den Tag legen.
(im übrigen: wenn man sich das anguckt, dann sieht man, dass DasDing und alle Implementierungen die gleiche öffentliche Schnittstelle haben, was widerum eine gemeinsame Basisklasse nahelegt...)