Schnittstellen in C++: Ein Leitfaden für alle, die das Googeln leid sind
\n\nWenn Sie jemals in Java oder C# programmiert haben und dann zu C++ gewechselt sind, haben Sie sich sicherlich gefragt: "Wo ist hier das Schlüsselwort interface?". Spoiler: Es gibt keins. In C++ werden Schnittstellen anders implementiert – über abstrakte Klassen. Und das ist kein Bug, sondern ein Feature, das unglaubliche Flexibilität bietet.
\n\nAuf Stack Overflow sammelt die Frage "How do you declare an interface in C++?" tausende Aufrufe. Das bedeutet, dass selbst erfahrene Entwickler manchmal verwirrt sind. Heute klären wir ein für alle Mal, wie man eine Schnittstelle in C++ deklariert, damit Ihr Code sauber ist und der Compiler zufrieden.
\n\nWas ist eine Schnittstelle in C++? (Spoiler: Es ist eine abstrakte Klasse)
\n\nIn der klassischen OOP ist eine Schnittstelle ein Vertrag. Eine Klasse, die die Schnittstelle implementiert, muss eine Implementierung aller ihrer Methoden bereitstellen. In C++ übernimmt diese Rolle die abstrakte Klasse – eine Klasse, die mindestens eine rein virtuelle Funktion (pure virtual function) enthält.
\n\nMerken Sie sich die Hauptregel:
\n- \n
- Schnittstelle = Klasse, in der alle Methoden rein virtuell sind. \n
- Abstrakte Klasse = Klasse, in der mindestens eine Methode rein virtuell ist (aber es können auch normale Methoden mit Implementierung vorhanden sein). \n
Für eine reine Schnittstelle verwenden wir die erste Variante: keine Logik, nur Deklarationen.
\n\nWie deklariert man eine reine Schnittstelle: Grundlegende Syntax
\n\nHier ist ein minimales Beispiel einer Schnittstelle für eine geometrische Figur:
\n\nclass IShape {\npublic:\n virtual double area() const = 0; // Rein virtuelle Funktion\n virtual double perimeter() const = 0; // Noch eine\n virtual ~IShape() {} // Virtueller Destruktor – obligatorisch!\n};\n\nBeachten Sie das = 0 am Ende der Deklaration. Das ist die magische Markierung "rein virtuell". Eine solche Methode hat keinen Rumpf (Implementierung) in der Basisklasse.
Wichtige Punkte:
\n- \n
- Präfix I im Klassennamen – eine ungeschriebene Konvention für Schnittstellen (I = Interface). Kann weggelassen werden, erleichtert aber das Lesen des Codes. \n
- Virtueller Destruktor ist obligatorisch. Wenn Sie ein Objekt über einen Zeiger auf die Schnittstelle löschen, ist das Verhalten ohne virtuellen Destruktor undefiniert (sprich: das Programm stürzt ab). \n
- Alle Methoden sind public – die Schnittstelle muss von außen aufrufbar sein. \n
Implementierung der Schnittstelle: Von der Theorie zur Praxis
\n\nJetzt schreiben wir eine Klasse, die unsere Schnittstelle IShape implementiert. Nehmen wir einen Kreis:
class Circle : public IShape {\nprivate:\n double radius;\npublic:\n explicit Circle(double r) : radius(r) {}\n\n double area() const override {\n return 3.14159 * radius * radius;\n }\n\n double perimeter() const override {\n return 2 * 3.14159 * radius;\n }\n};\n\nDas Schlüsselwort override (C++11 und neuer) ist nicht zwingend, aber sehr empfehlenswert. Es zwingt den Compiler zu prüfen, ob Sie tatsächlich eine virtuelle Methode der Basisklasse überschreiben. Haben Sie sich beim Namen vertippt? Der Compiler sagt: "Fehler! Diese Methode gibt es in der Schnittstelle nicht".
Polymorphismus in Aktion
\n\nJetzt kommt der spannende Teil – wir können mit verschiedenen Figuren über eine einheitliche Schnittstelle arbeiten:
\n\nvoid printShapeInfo(const IShape& shape) {\n std::cout << "Area: " << shape.area() << "\\n";\n std::cout << "Perimeter: " << shape.perimeter() << "\\n";\n}\n\nint main() {\n Circle c(5.0);\n printShapeInfo(c); // Funktioniert!\n return 0;\n}\n\nDie Funktion printShapeInfo nimmt eine Referenz auf IShape. Es ist ihr egal, welches konkrete Objekt übergeben wird – Kreis, Quadrat oder Dreieck. Solange sie die Schnittstelle implementieren, funktioniert alles. Das ist Polymorphismus