abstakte Klasse, konstruktor, Destruktor



  • Spitfire85 schrieb:

    Hallo,

    ich habe ein Problem mit den Konstruktoren und Destruktoren.

    Ich habe 1. eine abstrakte Klasse "AbstractFilter"
    Diese enhält eine virtuelle methoden. einen Konstruktor und einen virtual destruktor

    "virtual" alleine reicht ja nicht um die KLasse "abstrakt" zu machen; dazu braucht es ja solche Konstrukte:

    class Foo {
    public:
        virtual void bar() = 0; // aka "pure virtual" aka "abstract" 
    };
    

    Es stellt sich also zunächst die Frage welche der von Dir deklarierten Methoden nut "virtual" und welche "pure virtual" sind.

    Ein "pure virtual" Destruktor wäre übrigens keine gute Idee; genauso wie der Aufruf von virtuellen Methoden (gleich ob "pure" oder nicht) in Konstruktoren.

    Desweiteren: Zeig den Code!

    Grüsse

    *this



  • Sorry, ich habe den code leider nicht griffbereit, da er auf linux auf meinem laptop läuft un ich da noch nicht ins internet komme.

    @Gast++

    "virtual" alleine reicht ja nicht um die KLasse "abstrakt" zu machen; dazu braucht es ja solche Konstrukte:
    C/C++ Code:

    class Foo {
    public:
        virtual void bar() = 0; // aka "pure virtual" aka "abstract"
    };
    

    genauso habe ich es doch gemacht.

    desweiteren wollte ich noch erwähnen, dass der Fehler beim linken erst auftritt, wenn ich ein objekt der klasse HistogramR instanziiere.
    und zwar.

    HistogramR neu; //liefert fehler
    HistogramR neu(); //liefert keinen fehler
    aber: wenn ich dann neu.process() machen will, kommt process is not a method of HistogramR()(). obwohl ich process als methode habe.
    Aber das komische sind ja die 2 klammern dahinter()() woran liegt das?

    mfg
    Daniel



  • Du musst deinen virtuellen dtor auch irgendwo implementieren, selbst wenn er nix tut.
    Wenn eine Basisklasse allerdings bereits einen virtuellen dtor hat brauchst du den virtuellen dtor in der abgeleiteten Klasse nur wenn er "nicht leer" ist, ansonsten lässt du den einfach weg.

    Das gilt natürlich für alle virtuellen Funktionen die nicht "pure" sind, die müssen alle implementiert sein, sonst spuckt der Linker Zähne. Wenn sie "pure" sind müssen sie nicht implementiert sein, dürfen aber.

    Ausnahme: ein dtor muss *immer* implementiert werden, auch wenn er "pure" ist (wird ja von der abgeleiteten Klasse "direkt" aufgerufen).



  • Hallo,

    ich bin noch ein ziemlicher anfänger mit c++, könnt ihr vielleicht mal erklären, was genau der unterschied zwischen pure virtual und virtual ist?

    @hustbear:
    Danke für die erklärung, und wie gilt das ganze für einen konstruktor?



  • @Spitfire85:
    Lös es erstmal mittels Hardware: Netzwerkkabel (crossed) von Laptop zu PC oder Diskette oder IR
    Dann File kopieren; Source posten.

    @all:
    Da sage nochmal einer was gegen meine Glaskugel... 😃



  • Spitfire85 schrieb:

    ich bin noch ein ziemlicher anfänger mit c++, könnt ihr vielleicht mal erklären, was genau der unterschied zwischen pure virtual und virtual ist?

    Das müsste eigentlich in deinem C++-Buch/-Tutorial stehen. Wenn nicht, kannst du hier (und im nächsten Kapitel) nachschauen.

    Danke für die erklärung, und wie gilt das ganze für einen konstruktor?

    Konstruktoren können nicht virtual sein.



  • Ein "pure virtual" Destruktor wäre übrigens keine gute Idee; genauso wie der Aufruf von virtuellen Methoden (gleich ob "pure" oder nicht) in Konstruktoren.

    Ein virtual pure dtor ist überhaupt garkein Problem nicht 😉

    Ist auch ein ganz anderer Fall als ein Aufruf einer virtuellen Funktion im ctor, denn in einem Fall wo der dtor wirklich "virtual" verwendet wird ist das Objekt immer schon vollständig konstuiert, und der (implizite) Aufruf der Basisklassen dtoren (schönes Wort *g*) wird auch nie "virtual" gemacht.

    Ein "pure" dtor macht die Klasse nur abstrakt (wenn sie das nicht schon ist), sonst gibt es keinen mir bekannten Unterschied zwischen einem "virtual" dtor und einem "pure" dtor.



  • michba schrieb:

    Konstruktoren können nicht virtual sein.

    "...aber man kann den gewünschten Effekt leicht erzielen" (Bjarne Stroustrup C++PL 4.Auflage §15.6.2).
    Ween irgendwo in der englischsprachigen Literatur von "virtual Constructors" die Rede ist dann meint das wahrschinelich jenen Cloning-Mechanismus. (BS a.a.O.)

    Allerdings bezweifle ich ich dass der OP diesen Mechanismus wirklich braucht.



  • hustbaer schrieb:

    Ein "pure" dtor macht die Klasse nur abstrakt (wenn sie das nicht schon ist), sonst gibt es keinen mir bekannten Unterschied zwischen einem "virtual" dtor und einem "pure" dtor.

    Ein "pure" dtor wird wohl nichts aufräumen können, oder? 😃
    Allerdings hab ich sowas auch noch nie ausprobiert; ich sah bisher nie Veranlassung dazu.

    Einen "pure virtual dtor" (wenn das überhaupt geht) würde ich zumindest nicht verwenden um die Abstraktheit einer Klasse sicherzustellen. Das würde imo ungünstig delegieren:
    "Wenn Du's in der Kindklasse aufgeräumt kriegst darfst Du's auch benutzen" ???



  • Oh-oh, hier besteht offensichtlich einiges an Verwirrung. Mal sehen ob ich das verständlich machen kann...

    1. "pure" impliziert "virtual", wenn ich "pure" dtor schreibe meine ich also "virtual pure" dtor.

    2. "pure" tut nicht ganz das was die meisten denken. Wenn ich eine Memberfunktion F in einer Klasse B "pure" mache, dann ...
      I) ... bedeutet das dass die Klasse B "abstract" wird, also selbst nicht instanzierbar ist, und F erst in einer abgeleiteten Klasse überschrieben werden muss damit die abgeleitete Klasse nichtmehr "abstract" ist.
      II) ... bedeutet das dass die Funktion B::F nicht implementiert werden muss.

    Es bedeutet aber nicht dass die Funktion B::F nicht implementiert werden kann bzw. darf, und es bedeutet auch nicht dass sie nicht aufgerufen werden kann.
    Folgendes ist z.B. völlig OK:

    class B
    {
    public:
        virtual void F() = 0
        {
            printf("blubb\n");
        }
    
        void Test234()
        {
            this->B::F();
        }
    };
    
    class D : public B
    {
    public:
        virtual void F()
        {
            this->B::F();
        }
    };
    

    So. Nu sehen wir uns nochmal den Fall eines virtual pure dtors an.
    Zu (I): da jede Klasse einen dtor hat (wenn man selbst keinen schreibt wird ja einer automatisch generiert) ist das "muss überschrieben werden" schonmal für jede abgeleitete Klasse erfüllt. Durch die "virtual bleibt virtual" Regel ist dieser dtor der abgeleiteten Klasse auch wieder virtual, nur so nebenbei.

    Zu (II): wenn D von B abgeleitet ist, dann ruft der dtor D::~D implizit B::~B auf. Und zwar nicht per "virtual call", sondern ala "this->B::~B()", also namentlich. Anders gesagt: das "virtual" wird umgangen. Das muss auch so sein, da sonst ja D::~D sich selbst aufrufen würde, was nicht im Sinne des Erfinders wäre.
    Ergo: B::~B wird aufgerufen, daher muss B::~B auch irgendwo implementiert sein, sonst wird das Programm nicht linken.
    Dass B::~B dabei "pure" deklariert ist macht weiter keinen Unterschied, es funktioniert also alles so wie es funktionieren würde wenn B::~B NICHT "pure" wäre -- eben mit dem einzigen Unterschied dass B "abstract" wird.

    Beispiel:

    #include <conio.h>
    #include <memory>
    
    class B
    {
    public:
    	virtual ~B() = 0
    	{
    		printf("B::~B\n");
    	}
    	virtual void pure() = 0
    	{
    		printf("pure\n");
    	}
    };
    
    class D : public B
    {
    public:
    	virtual ~D()
    	{
    		printf("D::~D\n");
    	}
    	virtual void pure()
    	{
    		printf("override-of-pure\n");
    	}
    };
    
    int main() 
    {
    	{
    		D test234;
    		test234.B::pure();
    		test234.pure();
    	}
    	{
    		std::auto_ptr<B> test234(new D);
    		test234->B::pure();
    		test234->pure();
    	}
    	_getch();
    	return 0;
    }
    

    Output:

    pure
    override-of-pure
    D::~D
    B::~B
    pure
    override-of-pure
    D::~D
    B::~B
    

    Kurz gesagt: nein, ein "virtual pure dtor" ist überhaupt kein Problem. Kann sogar recht nützlich sein, nämlich wenn man eine Klasse "abstract" machen möchte ohne alle abgeleiteten Klassen zu zwingen irgendeine bestimmte Memberfunktion zu überschreiben.

    p.S.: zum Thema 'ein "pure" dtor wird wohl nichts aufräumen können': natürlich kann er das. Eine abstrakte Klasse kann genauso State (=Datenmember) haben der "aufgeräumt" werden muss wie eine normale Klasse.



  • hustbaer schrieb:

    Folgendes ist z.B. völlig OK:

    class B
    {
    public:
        virtual void F() = 0
        {
            printf("blubb\n");
        }
    
        void Test234()
        {
            this->B::F();
        }
    };
    

    Nicht ganz. Eine pure virtual function darf zwar, wie du richtig sagtest, eine Implementation besitzen, diese muss dann aber außerhalb der Klasse stehen:

    class B
    {
    public:
        virtual void F() = 0;
        void Test234()
        {
            this->B::F();
        }
    };
    
    // irgendwo - am Besten in einer cpp-Datei
    void B::F() {
      printf("blubb\n");
    }
    

    Die Syntax virtual void foo() = 0 { /* Impl */ } ist kein gültiges C++.



  • Danke für den Hinweis! Ich wusste das nicht und der VC 8 die dumme Gurke frisst es natürlich trotzdem.

    Ist aber auch wieder so eine von diesen "vollkommen für nix" Regeln.



  • Spitfire85 schrieb:

    ...
    HistogramR neu; //liefert fehler
    HistogramR neu(); //liefert keinen fehler
    aber: wenn ich dann neu.process() machen will, kommt process is not a method of HistogramR()(). obwohl ich process als methode habe.
    Aber das komische sind ja die 2 klammern dahinter()() woran liegt das?...

    Das liegt daran, dass Du "neu" nicht als "Histogramm", sondern als "Funktion ohne Parameter und Rückgabewert Histogramm" deklariert hast.
    Vergleiche mal:

    int f(int);   // Funktion nimmt int, returniert int
    int f();      // Funktion "nimmt void", returniert int
    Histogramm f();   // Funktion "nimmt void", returniert Histogramm
    Histogramm neu();  // ebenso....
    

    dann wird's vielleicht klarer.
    Ist ein beliebter Fehler, weil es bei "dynamischer Allozierung" nicht dazu kommt:

    Histogramm* neu_dynamisch = new Histogramm(); // ist erlaubt und identisch mit "new Histogramm;"
    
    Histogramm neu_Funktion(); // Funktion
    Histogramm neu_Var; // Das, was Du urspünglich meintest
    

    Gruß,

    Simon2.


Anmelden zum Antworten