C++ Interface Abstrakte Methoden



  • Szenario:
    Interface

    • Abstrakte Methode A
    • Abstrakte Methode B

    Klasse ExampleNo1 : public Interface

    • Initalisieren von Abstrakter Methode A und B

    Klasse Example No2: public Interface

    • Initalisieren nur Abstrakte Methode A und Abstrakte Methode B weglassen wird nicht benötigt

    Frage: Wie kann ich das Szenario in die Realität umstellen das ich später im Interface Abstrakte Methoden habe die in einer abgeleiteten Klasse benötigt werden aber in einer anderen nicht ?

    Schonmal vielen danke im voraus und ein schönes Wochenende / Woche 🙂



  • Du kannst statt der abstrakten Methode (d.h. du meinst damit sicherlich "pure virtual") eine virtuelle Methode mit Standardimplementierung anbieten.
    Oder wenn das Interface keine Implementierung enthalten soll, dann eine Basisklasse damit erstellen und dort dann die Methoden implementieren (und die weiteren Klassen erben dann von dieser Basisklasse).



  • @Sebi1412 sagte in C++ Interface Abstrakte Methoden:

    Frage: Wie kann ich das Szenario in die Realität umstellen das ich später im Interface Abstrakte Methoden habe die in einer abgeleiteten Klasse benötigt werden aber in einer anderen nicht ?

    Ich kann nicht umhin zu fragen: Warum???



  • @Mitleid
    Ist für einer meiner Labor Aufgaben im Studium, habe das Problem einfach dadurch gelöst indem
    ich die übergeben Abstrakte Methode vom Interface einfach implementiere die anderen übergeben Klassen und in Funktionsblock rein schreibe cout << "Fehler"; was soweit auch ausreichenden für die Labor Aufgabe war.

    Jedoch interessiert es mich ob es eine einfache Möglichkeit gibt von allen geerbten Abstrakten Methoden vom Interface sich nur ein paar auszusuchen die man in die abgeleitete Klasse implementiert und die anderen die man nicht braucht "ignoriert" so gesehen



  • @Sebi1412

    Indem du sie nur virtuell und nicht abstrakt machst. In der Basisklasse implementierst du dann einen leeren Funktionsrumpf und in der abgeleiteten Klasse überschreibst du die geforderten Funktionen.
    Wenn du das Interface nicht anfassen darfst oder kannst baust du dir eine Zwischenschicht, die alle Funktionen des Interface als "No-Op" implementiert, und leitest deine endgültigen Klassen dann von dieser Zwischen-Klasse ab. Deine endgültigen Klassen können dann nur die gewünschten Funktionen implementieren.
    Ich bin aber auch neugierig, warum manche abstrakte Funktionen nicht implementiert werden sollen, das könnte auf ein verbesserungswürdiges Design hinweisen, wo das Interface mehrere Funktionen übernimmt, die besser in mehrere Interfaces aufgeteilt werden könnten.



  • @DocShoe

    Aus Abstrakte Methoden reine Virtualle Methoden machen, die man somit nicht "Implementieren" muss verstehe ich soweit und auch der Vorschlag eine zwischen Klasse einzubauen und dann erst die abgeleiteten Methoden zu implementieren.

    Zu der frage "Warum manche Abstrakte Funktionen nicht implementiert werden sollen?". Sowie ihr es vorgeschlagen habt würde ich es bei jedem anderen System auch machen, jedoch ist man bei unseren Labor-Aufgaben "teilweise" beschränkt, dort wird vorgegeben man soll ein Interface benutzten und auch das DIP (Dependecy Inversion Principle) benutzen (damit fällt schonmal Basisklasse und dann abgeleitete Klasse raus sowie der erste Vorschlag keine Abstrakten Methoden zu benutzen) deshalb wollte ich mal nachfragen ob es dazu eine "einfache" Lösung gibt.

    Meine Idee dahinter war das es vielleicht eine Funktion aus der Standardbibilothek gibt die es zulässt die jeweiligen Abstrakten Methoden zu ignorieren



  • @Sebi1412 sagte in C++ Interface Abstrakte Methoden:

    Meine Idee dahinter war das es vielleicht eine Funktion aus der Standardbibilothek gibt die es zulässt die jeweiligen Abstrakten Methoden zu ignorieren

    Das überprüft schon der Compiler, d.h. es gibt dann eine Fehlermeldung, wenn es keine Implementierung in der abgeleiteten Klasse gibt.
    Falls man doch zur Laufzeit direkt eine "pure virtual" Funktion (der Basisklasse) aufruft, erhält man aber einen Laufzeitfehler, z.B. bei MSVC R6025.



  • @Sebi1412 sagte in C++ Interface Abstrakte Methoden:

    Jedoch interessiert es mich ob es eine einfache Möglichkeit gibt von allen geerbten Abstrakten Methoden vom Interface sich nur ein paar auszusuchen die man in die abgeleitete Klasse implementiert und die anderen die man nicht braucht "ignoriert" so gesehen

    Wie DocShoe schon schreibt, wird das vom Compiler geprüft. Sobald virtual davor steht muss die Implementierung vorhanden sein.

    Wenn Du z.B. einen Zeiger oder einer Referenz des Basisklassentyps (hier dein Interfacetyp) verwendest, kann der Compiler nicht wissen ob sich dahinter zur Laufzeit nicht doch ein Objekt des abgeleiteteten Typs befindet, in welchem keine Implementierung vorhanden ist. Daher muss irgendwo in der Vererbungshierarchie eine Definition deiner Methode vorhanden sein, an die zur Laufzeit dynamisch gebunden werden kann.

    Wäre das was Du willst möglich, würde das auch das Liskovsche Substitutionsprinzip (LSP) verletzen. Steht bei Robert C. Martin direkt im Kapitel vor dem Dependency-Inversion Princip (DIP).

    Wenn DocShoe von einem verbesserungswürdigen Design schreibt, denke ich auch, dass Du dich da verrannt hast. Mach es anders. Das DIP ist eigentlich eine simples Sache. Glaube Du denkst hier schon zu kompliziert.



  • @Sebi1412 Ein abstraktes Interface sollte möglichst keine Methoden haben die es in einigen abgeleiteten Klassen "nicht gibt". Denn über die abstrakte Interface-Klasse kann man sie ja immer aufrufen - das kann die konkrete Klasse ja nicht verhindern.

    In einigen Fällen kann es aber natürlich Sinn machen. z.B. findet man bei Streams oft Methoden wie "seek", die bei manchen Stream-Arten halt einfach nicht gehen - z.B. Netzwerk-Streams. Die übliche Variante ist dann diese "verbotenen" Methoden eine Exception werfen zu lassen.

    Bzw. manchmal kann es auch Sinn machen die Methode einfach leer zu implementieren. Beim Beispiel der Stream-Klasse könnte ein konkreter Stream z.B. seine "flush" Methode leer implementieren, wenn er nichts puffert und daher in "flush" auch einfach nichts zu tun hat.



  • Um es nun einmal konkrete zu machen übersetzbarer Code, der trotzdem faktisch nichts macht. Die notwendigen Destruktoren etc. sind weggelassen es geht nur um die Member Functions. Wenn Du wie hier schon besprochen in ConcreteClass2 nicht die zweite Methode überschreibst, dann wird der C++ Compiler das Programm nicht übersetzen. Ein Workaround (das ist der Teil der durch die Präprozessor Variable WORKAROUND nur bedingt übersetz wird) ist es die Methode leer zu implementieren/eine Exception zu werfen was hier im Code einfach durch donothing signalisiert wird.

    Die Alternative ist es, sofern das Sinn ergibt, zwei abstrakte Interface-Klassen zu definieren.

    #include <iostream>
    #include <cstdlib>
    
    class Interface1 {
    public:
    	virtual void seek() = 0;
    	virtual void add(int i) = 0;
    };
    
    class ConcreteClass1 : public virtual Interface1 {
    public:
    	void seek() override;
    	void add(int i) override;
    };
    
    void
    ConcreteClass1::seek() {
    	std::cout << "ConcreteClass1::seek\n";
    }
    
    void
    ConcreteClass1::add(const int i) {
    	std::cout << "ConcreteClass1::add\n";
    }
    
    class ConcreteClass2 : public virtual Interface1 {
    public:
    	void seek() override;
    #ifdef WORKAROUND
    	void add(int i) override;
    #endif
    };
    
    void
    ConcreteClass2::seek() {
    	std::cout << "ConcreteClass2::seek\n";
    }
    
    #ifdef WORKAROUND
    void
    ConcreteClass2::add(int i) {
    	std::cout << "ConcreteClass2::add -- do nothing\n";
    }
    #endif
    
    class PartialInterface1 {
    public:
    	virtual void seek() = 0;
    };
    
    class PartialInterface2 {
    public:
    	virtual void add(int i) = 0;
    };
    
    class ConcreteClass3 : public virtual PartialInterface1, PartialInterface2 {
    public:
    	void seek() override;
    	void add(int i) override;
    };
    
    void
    ConcreteClass3::seek() {
    	std::cout << "ConcreteClass3::seek\n";
    }
    
    void
    ConcreteClass3::add(int i) {
    	std::cout << "ConcreteClass3::add\n";
    }
    
    int main () {
    	ConcreteClass1 cc1;
    
    	cc1.seek();
    	cc1.add(1);
    
    	ConcreteClass2 cc2;
    
    	cc2.seek();
    	cc2.add(1);
    
    	ConcreteClass3 cc3;
    
    	cc3.seek();
    	cc3.add(1);
    
    	return EXIT_SUCCESS;
    }
    

Anmelden zum Antworten