new operator mit Zeiger auf zu erstellende Klasse



  • Hallo.

    ich hab einen ganzen Sack voll abgeleiteter Klassen, alle vom selben Vater.

    also:
    Class a
    Class aa:a
    Class ab:a
    Class ac:a..........

    Ich möchte dann eine Instanz zB von af erzeugen
    also:
    p = new af();

    Nun könnte ich einen Zeiger zum erzeugen gebrauchen.
    so in der Form:
    p = new *welcheClass();

    So gehts wahrscheinlich nicht, aber wenn wo bekomme ich solch einen Zeiger her?
    Schön wenn jemand eine Idee hätte. Ich hab gehört, mit Delphi solls sowas geben.
    Rudi



  • Das nennt sich Klassenreferenzen und virtuelle Konstruktoren, und in C++ gibt es das nicht. Du müßtest das von Hand machen (z.B., indem du eine eigene Metaklassenhierarchie mit virtuellen Factory-Funktionen schreibst).

    Wenn du allerdings C++Builder verwendest, ist das mit Einschränkungen machbar. Derzeit gibt es zwar keine Möglichkeit, in C++ über eine Klassenreferenz einen virtuellen Konstruktor aufzurufen, da Klassenreferenzen in C++ noch nicht hierarchisch typisiert sind. Einen Wrapper für den virtuellen Konstruktor kannst du aber in Delphi schreiben. Klassisches Beispiel wäre folgender Konstruktorwrapper für TComponent (TComponent.Create ist virtuell):

    unit VirtualConstructorWrappers;
    
    interface
    
    uses
      Classes;
    
    function CreateComponent (ComponentClass: TComponentClass; Owner: TComponent): TComponent;
    
    implementation
    
    function CreateComponent (ComponentClass: TComponentClass; Owner: TComponent): TComponent;
    begin
      Result := ComponentClass.Create (Owner);
    end;
    
    end.
    
    #include <Classes.hpp>
    #include <StdCtrls.hpp>
    
    #include <cstdlib>
    
    #include "VirtualConstructorWrappers.hpp"
    
    static const TComponentClass componentClasses[] = {
        __classid (TButton),
        __classid (TCheckBox),
        __classid (TEdit),
        __classid (TLabel),
    };
    
    TComponent* createRandomComponent (TComponent* Owner)
    {
        unsigned componentCount = sizeof (componentClasses) / sizeof (componentClasses[0]);
        return CreateComponent (std::rand () % componentCount);
    }
    


  • Hallo, schaut doch gut aus, doch bevor ichs einbaue hab ich noch ein Randproblem. Diese Lösung schau nicht so aus, als obs ich selbst machen könnte.

    1.) Ich nehme an, mit dem Returnzeiger kann mit delete die Instanz wieder löschen.
    2.) Kann man damit auch ein Array einer Klasse erzeugen?
    3.) Wie mache ich es, wenn ich keine Instanz der Klasse erzeugen möchte, aber eine vorhandene virtuelle static Funktion aufrufen möchte?

    Hoffentlich wird es jetzt nicht zu kompliziert 🙄
    Rudi



  • @audacia:
    Ich würde das mit templates lösen...

    template<typename BaseClass>
    struct Class {
      virtual BaseClass* create() = 0;
    };
    
    template<typename ConcreteClass, typename BaseClass>
    struct ConcreteClass : public Class<BaseClass> {
      virtual ConcreteClass* create() {}
    };
    

    wobei über einen template operator T könnte man hier auch den BaseClass Parameter fallen lassen...

    Generell sind templates bei sowas meistens eine super wahl. da wir eh keine reflection haben, brauchen wir auch keine reflection infrastruktur...



  • rudiM schrieb:

    1.) Ich nehme an, mit dem Returnzeiger kann mit delete die Instanz wieder löschen.

    Ja.

    rudiM schrieb:

    2.) Kann man damit auch ein Array einer Klasse erzeugen?

    Nein. Das hat allerdings mit dieser Thematik nichts zu tun; das Kombinieren von new[] und Polymorphie ist grundsätzlich nicht sinnvoll.

    rudiM schrieb:

    3.) Wie mache ich es, wenn ich keine Instanz der Klasse erzeugen möchte, aber eine vorhandene virtuelle static Funktion aufrufen möchte?

    Virtuelle statische Funktionen sind zwar in C++-Code aufrufbar, aber nur für Objekte oder Klassen, nicht für Klassenreferenzen. Dafür müßtest du dir eine Basisklasse in Delphi schreiben, z.B.:

    type
      TGraphicClass = class of TGraphic;
      TGraphic = class
      public
        constructor Create (); virtual; abstract;
        constructor CreateFromStream (AStream: TStream); virtual; abstract;
        class function SupportsFileFormat (FormatExt: String): Boolean; virtual; abstract;
    
          // Wrappers for C++ code
        class function CppCreate (GraphicClass: TGraphicClass); static;
        class function CppCreateFromStream (GraphicClass: TGraphicClass; Stream: TStream); static;
        class function CppSupportsFileFormat (GraphicClass: TGraphicClass; FormatExt: String); static;
      end;
    
    ...
    
    class function TGraphic.CppCreate (GraphicClass: TGraphicClass);
    begin
      Result := GraphicClass.Create ();
    end;
    class function TGraphic.CppCreateFromStream (GraphicClass: TGraphicClass; Stream: TStream);
    begin
      Result := GraphicClass.CreateFromStream (Stream);
    end;
    class function TGraphic.CppSupportsFileFormat (GraphicClass: TGraphicClass; FormatExt: String);
    begin
      Result := GraphicClass.SupportsFileFormat (FormatExt);
    end;
    
    class TMyBitmap : public TGraphic
    {
    public:
        virtual TMyBitmap (void);
        virtual TMyBitmap (TStream* AStream);
        virtual __classmethod bool SupportsFileFormat (String FormatExt);
    };
    
    ...
    
        std::vector <TGraphicClass> gc;
        gc.push_back (__classid (TMyBitmap));
        ...
        for (unsigned i = 0; i != gc.size (); ++i)
            if (TGraphic::CppSupportsFileFormat (gc, theFileExt))
                return TGraphic::CppCreateFromStream (gc, theStream);
        return 0;
    

    __classmethod ist neu in C++Builder 2009; anscheinend gibt es auch Bestrebungen, in einer der kommenden Versionen Klassenreferenzen auch in C++ vollständig zu typisieren, so daß obige Wrapper überflüssig würden.

    @Shade: das ist im Prinzip, was ich oben erwähnte: "Du müßtest das von Hand machen (z.B., indem du eine eigene Metaklassenhierarchie mit virtuellen Factory-Funktionen schreibst)."

    Warum du da jetzt aber ein Template als Basisklasse benutzen willst, erschließt sich mir nicht ganz. Das Template wäre ohnehin nur für die Klassen tauglich, die sinnvoll ohne Parameter konstruierbar sind, und für die du keine statischen virtuellen Funktionen benötigst.

    Shade Of Mine schrieb:

    da wir eh keine reflection haben, brauchen wir auch keine reflection infrastruktur...

    Jetzt mal langsam. Bei Reflection sind wir doch noch gar nicht 😉



  • audacia schrieb:

    @Shade: das ist im Prinzip, was ich oben erwähnte: "Du müßtest das von Hand machen (z.B., indem du eine eigene Metaklassenhierarchie mit virtuellen Factory-Funktionen schreibst)."

    Warum du da jetzt aber ein Template als Basisklasse benutzen willst, erschließt sich mir nicht ganz. Das Template wäre ohnehin nur für die Klassen tauglich, die sinnvoll ohne Parameter konstruierbar sind, und für die du keine statischen virtuellen Funktionen benötigst.

    Ne ne.
    Dank den Templates biete ich generische create() methoden an, die man fuer fall zu fall trivial neu definieren kann. und da ich templates habe, brauche ich keine runtime type liste.

    und natuerlich kann ich dann auch virtuelle klassen-funktionen anbieten, da ich ja mit metaklassen agiere und ich hier problemlos auch laufzeit polymorphie anbieten kann.

    ob die basisklasse selber ein template sein soll oder lieber nicht, muss man sich ueberlegen. ist beides technisch ja trivial.

    ist dank templates wirklich kein grosser aufwand und laeuft auf allen compilern. was man halt nicht hat ist reflection. aber so simple meta-creators sind kein problem. man braucht dazu dank templates ja auch keine hierachie selber schreiben, sondern instanziiert die klassen einfach mit dem passenden typen 😉



  • Ah, jetzt erschließt sich mir, was du meinst. Ja, dafür sind Templates durchaus sinnvoll.

    Nur:

    Shade of Mine schrieb:

    und da ich templates habe, brauche ich keine runtime type liste.

    ?
    Brauche ich die denn?



  • audacia schrieb:

    Brauche ich die denn?

    Aus der Frage schliesse ich mal, dass du keine brauchst.
    Dachte zwar dass das noetig waere aber wenn dem nicht so ist: gut 🙂


Anmelden zum Antworten