Operator new[] für abgeleitete Klassen



  • Hallo,
    Jetzt bin ich eine Woche dran. Langsam denk ich an einen Compillerfehler, aber wie kann ich ihn umgehen? Gibt's eine anderer Methode?
    Ich hab C++Bulilder 2009 prof. Ich hab letzthin den neuesten ausprobiert und gleich wieder weggesch(m)issen. Ausser ein paar nutzlosen Gimmics hat der noch die gleichen Muggen.

    Also ich hab eine Basiklasse, bis auf den Konstruktor und die public Basisdaten(600Byte) virtuell. Die Subklassen haben mehr versch. private Daten.
    Im der Anwendung brauch ich ein eindim. Array von Subklassen.

    Die beste Lösung bisher ist für jede Subklasse ?? die Definition von:
    void* operator new[] ( size_t Subklass?? ) { return malloc( Subklass?? ); };

    Eine Abfrage mit sizeof( Subklass?? ) bringt für alle den richtigen Wert.

    Wenn ich mit dem Debugger folgendes durchsteppe:

    Ptr = new Subklass??[ 1000 ];

    sehe ich, dass der jeweilige operator new[] augerufen wird. Der gelieferte Pointer ist erst mal ok. Wenn ich nach der Erstellung des Pointers die Daten dazu ansehe fehlen aber immer die privaten Daten der Subklassen. Rufe ich eine Subklassenfunktion auf, sind alle Daten von Basis- und Subklasse da.

    Soweit so gut.
    Wenn ich nun in der Anwendung den Ptr++ iteriere, dann ist das nächste Element immer nur mit dem Displacement der Basisklasse weitergerechnet.
    Das wäre zu korrigieren! ..denn dann schlägt die Exception zu!

    Ich bin mir sicher, ihr wisst eine Lösung.

    PS.: mit std::vector bin ich auch nicht weitergekommen.

    Gruß Rudi



  • Ich bin mir nicht sicher, ob ich dich ganz verstehe, aber ich denke, du hast ein Object slicing Problem. Du kannst Arrays nur aus genau einem Datentyp erzeugen, Mischen ist nicht möglich. Wenn jetzt ein Array aus [DT1,DT2,DT3...] bauen möchtest, musst du dir eine geeignete Datenstruktur überlegen. Da gibt´s verschiedene Ansätze (std::any, std::variant, Zeiger auf die Basisklasse).

    Weil ich deine Anforderungen nicht genau kenne kann ich nur raten, dass ein std::vector<std::unique_ptr<BaseClass>> oder std::vector<std::shared_ptr<BaseClass>> die Lösung sein könnten.



  • Hallo Doc
    Nichts derartiges. Einfach eine Basisklasse und viele Subklassen. wobei immer nur ein Typ im Array vorkommt.
    Mein Problem könnte gelöst werden, wenn ich aus den Subklassen einfache mache. Aber dann brauche ich in der Anwendung auch verschiedene Pointertypen, die das Programm kompliziert machen. Das will das Klassenkonzept ja verhindern.
    Eine andere Möglichkeit, die so vom Klassenkonzept überhaupt nicht gewollt ist, ist alle privaten Subklassenvariablen in den public Bereich der Basisklasse zu verschieben. Funktioniert, ist aber unelegant und braucht auch für die kleinen Klassen den selben Speicherplatz. (die zugriffszeiten noch nicht berücksichtigt )



  • Von welchem Typ ist denn Ptr?

    Aber warum du den operator new[] brauchst, ist mir nicht klar.



  • Irgend wie geht aus der Frage nicht hervor, weshalb Operator new überladen werden soll. Üblicherweise macht man das nur, wenn man spezielle Anforderungen an die Speicheranforderung hat. Beispiele für solche speziellen Anforderungen sind, dass man nicht die Standard Anforderung über malloc haben will, sondern Speicher in besonderer Form anfordern will z.B. numa_alloc anstatt malloc.



  • @RudiMBM sagte in Operator new[] für abgeleitete Klassen:

    Im der Anwendung brauch ich ein eindim. Array von Subklassen.

    Die beste Lösung bisher ist für jede Subklasse ?? die Definition von:
    void* operator new[] ( size_t Subklass?? ) { return malloc( Subklass?? ); };

    warum geht nicht:

    std::vector<Subklass> SubklassVector;
    

    ?



  • @RudiMBM sagte in Operator new[] für abgeleitete Klassen:

    Einfach eine Basisklasse und viele Subklassen. wobei immer nur ein Typ im Array vorkommt.

    Warum nicht

    using GenericSubClass = std::variant<SubClassA, SubClassB, SubClassC>;
    
    std::vector<GenericSubClass> Elements;
    

    ?



  • Mal was Prinzpielles:
    Leitest du von TObject ab? Poste doch bitte mal die Definition einer Subklasse und den Anwendungsfall, wo du ein Array aus Subklassen erzeugst. Bitte nur den relevaten Teil.



  • Hallo Alle zusammen
    Danke für Euere Antworten.
    @Th69 Es ist der Versuch den Fehler zu beheben, hat es aber nicht, ist ohne operator new[] das selbe.
    @john-0 dito.
    @Belli Geht schon... macht aber den selben Fehler.
    @Quiche-Lorraine Interessant. Wie schaut der Speicherbedarf für GenericSubClass aus? Ich will zu einem Zeitpunkt nur eine SubClassArt haben, danach kann der speicher freigegeben werden. Der Speicherblock kann bei new[180000] mit je min.600 Byte groß werden.
    @DocShoe Das sind ganz einfache Classen die etwas berechnen ohne jeglichen VCL Kram. Die Anwendung:

    Basisclass* ErgArr = new Subclass??[ InstanzenAnzahl ];

    for ( i=0; i<InstanzenAnzahl; i++ ) {
    ............ErgArr->Dowas();
    ErgArr++; }

    Wenn ich einzelne Instanzen so erzeuge geht es, nur nicht bei einem Array.



  • Irgendwie finde ich deine Beiträge @RudiMBM hier gerade etwas wirr.

    Kannst du ein Minimalbeispiel geben (gerne ohne VCL Kram) das tendentiell kompilierfähig sein sollte, aber deinen Fehler provoziert?

    Du kannst auch gerne Code Tags benutzen einfach den Code markieren und auf </> oberhalb des
    Eingabefenster klicken.



  • @RudiMBM
    Im kann deinen Einsatzzweck überhaupt nicht nachvollziehen. Mache bitte mal ein kleines Beispiel.

    Der Speicherblock kann bei new[180000] mit je min.600 Byte groß werden.

    Heißt das du hast immer 180000 GenericSubClass-Instanzen, deren Größe mindestens 600 Byte groß sind?

    Warum nutzt du überhaupt new[]? Warum funktioniert hier kein std::vector, std::set, std::map?

    Ich will zu einem Zeitpunkt nur eine SubClassArt haben, danach kann der speicher freigegeben werden.

    Was meinst du damit? Willst du z.B. ein Element in Elements löschen können?



  • @RudiMBM sagte in Operator new[] für abgeleitete Klassen:

    @Belli Geht schon... macht aber den selben Fehler.

    Welchen?



  • Damit es klar wird:

    class C_Rechnen
    {
    public:
    							C_Rechnen();
    virtual						~C_Rechnen() = 0;
    virtual     void	    	NeueAuswertung( );
    virtual     void	    	Erstellen( int ) = 0;
    virtual     void	    	TestIter( ) = 0;
    			long double		innermax, innermin, outermax, outermin;
    			bigint			ax, ay;
    			int				itercount;
    };
    //------------------------------------------------------------
    class C_RechnenMB : public C_Rechnen
    {
    public:
    							C_RechnenMB();
    							~C_RechnenMB();
    			void*			operator new[]( size_t C_RechnenMB ) { return malloc( C_RechnenMB ); };
    
    			void	    	NeueAuswertung( );
    			void			Erstellen( int );
    };
    /------------------------------------------------------------
    class C_RechnenBB : public C_Rechnen
    {
    public:
    							C_RechnenMB();
    							~C_RechnenMB();
    			void*			operator new[]( size_t C_RechnenMB ) { return malloc( C_RechnenMB ); };
    
    			void	    	NeueAuswertung( );
    			void			Erstellen( int );
    private:
    			Weitere lokale Daten
    };
    /------------------------------------------------------------
    ..... weitere subclassen wie oben
    
    
    Anwendung;
    .......
    C_Rechnen   *ErgAnz, *ErgAnzArray;
    
    .....
    	switch ( Mode ) {
    		case 1:		ErgAnzArray = new C_RechnenMB[Anzahl];	break;
    		case 2:		ErgAnzArray = new C_RechnenBB[Anzahl];	break;
    		...
    		default :	ErgAnzArray = new C_Rechnen??[Anzahl];	break;
    		}
    
    .......
    ErgAnz = ErgAnzArray;
    for ( ky=0; ky<bsesy; ky++ ) {
    	for ( kx=0; kx<bsesx; kx++ ) {
    		ErgAnz->NeueAuswertung();
    		ErgAnz++;
    		}
    	}
    
    ErgAnz = ErgAnzArray;
    for ( ky=0; ky<bsesy; ky++ ) {
    	for ( kx=0; kx<bsesx; kx++ ) {
    		ErgAnz->Erstellen( 123 );
    		ErgAnz++;
    		}
    	}
    ....
    delete[] ErgAnzarray;
    ...
    @Belli 
    Wenn ich nun in der Anwendung den ErgAnz++ iteriere, dann ist das nächste Element immer nur mit dem Displacement der Basisklasse weitergerechnet.
    Das wäre zu korrigieren! ..denn dann schlägt die Exception zu!


  • Hast du dir den Quelltext mal angeschaut? Bitte gib dir Mühe und formatier´ ihn so, dass man ihn auch lesen kann.

    Dein echtes Problem ist aber, wie schon oben beschrieben, Object Slicing. Du erstellst zB ein Array aus C_RechnenMB und weist das einem C_Rechnen Pointer zu. Mit der Zuweisung verlierst du die Information über den korrekten Datentyp. Das hast du schon richtig bemerkt, dass der Zeiger bei der Pointerarithmetik auf ungültige Elemente zeigt, weil die Größe eines C_Rechnen-Objekts nicht der Größe eines C_RechnenMB entspricht.

    Beispiel:
    Klasse Base hat eine Größe von 16 Byte, Klasse Derived hat eine Größe von 24 Byte und ist von Base abgeleitet:

    Base* arr = new Derived[10]; // arr ist gültig und zeigt auf den korrekten Derived
    arr++;                       // der arr-Zeiger wird um die Größe von Base vergrößert (16Byte) und zeigt mitten in das erste Derived-Objekt
    

    Das Problem lässt sich auch nicht durch eine operator new Überladung lösen, sondern ist ein Designfehler. Wenn das Array immer von einem bestimmten Typ ist kannste das vllt über separate Funktionen lösen (vllt geht statischer Polymorphismus per templates), oder indem du jedes konkrete C_Rechnen Objekt auf dem Heap erzeugst. Die zweite Option würde ich vermeiden, wenn´s irgendwie anders geht.

    PS:
    Als Hack könntest du der C_Rechnen Klasse eine C_Rechnen* advance( C_Rechnen* current )Funktion spendieren, die den übergebenen Pointer um den korrekten Offset verschiebt:

    class C_Rechnen
    {
       virtual C_Rechnen* advance( C_Rechnen* current ) const = 0;
    };
    
    class C_RechnenMB
    {
       C_Rechnen* advance( C_Rechnen* current ) const override
       {
          // Pointer wird um die Größe der konkreten Klasse verschoben
          C_RechnenMB* tmp = dynamic_cast<C_RechnenMB*>( current );
          return tmp ? ++tmp : nullptr;
       }
    }
    
    // Pointer auf nächstes Element wird durch das Element selbst bestimmt.
    C_Rechnen* arr = new C_RechnenMB[2];
    arr = arr->advance( arr );
    

    Ist aber nur ne Krücke, die ich im Produktivcode nicht benutzen würde.

    Beispiel für statischen Polymorphismus per templates:

    template<typename Calculator>
    ... calculate( ... )
    {
       std::vector<Calculator> calculators( count );
       for( auto& c : calculators ) c.NeueAuswertung(); // kann man sich sparen, wenn man das bereits im Konstruktor tut
       for( auto& c : calculators ) c.Erstellen( 123 );
       return was_auch_immer;
    }
    
    ... calculate_results( ... )
    {
       if( mode == 1 )       return calculate<C_RechnenMB>( ... );
       else if( mode == 2 )  return calculate<C_RechnenBB>( ... );
       default :             return calculate<C_Rechnen??>( ... );
    }
    


  • @RudiMBM abgesehen davon, dass die Operatorüberladung garantiert nicht nötig ist, macht new[] in C_RechnenBB ein malloc( C_RechnenMB ) das sieht an der Stelle nach einem Copy & Paste Fehler aus.



  • @RudiMBM. Da ErgAnz (bzw. ErgAnzArray) ein C_Rechnen * ist, wird bei ErgAnz++ immer nur um sizeof(C_Rechnen) der Zeiger erhöht.
    Du müßtest einen char * verwenden und um sizeof(Subclass) (eigene Variable ' size' dafür verwenden) und dann nach += size wieder in den Basisklassenzeiger casten.

    Ist aber, wie auch @DocShoe schon geschrieben hat, ein Designfehler.
    Alternative wäre ein C_Rechnen **, d.h. ein Array von C_Rechnen * und d.h. wie auch schon von @DocShoe geschrieben, in modernem C++ ein std::vector<std::unique_ptr<C_Rechnen>>.



  • vielleicht:

    std::vector<*C_Rechnen> ErgAnz;
    for(size_t i = 0; i < Anzahl; ++i)
       ErgAnz.push_back(new C_RechnenMB/*C_RechnenBB ...*/);
    

    Edit: Okay, bin zu spät ...



  • @Belli sagte in Operator new[] für abgeleitete Klassen:

    std::vector<*C_Rechnen> ErgAnz;
    

    Ist das neues C++? 🙂



  • Ich glaube, das ist gebraucht ...
    Was gefällt Dir daran nicht, ist das falsch?

    Edit:
    Ja, ist falsch .... 🙃
    Es muss natürlich:

    std::vector<C_Rechnen*> ErgAnz;
    

    heißen ...
    Tschuldigung, aber ich bin keine 60 mehr ...



  • @Belli sagte in Operator new[] für abgeleitete Klassen:

    Tschuldigung, aber ich bin keine 60 mehr ...

    😱
    Sprich mich an, ich helfe dir über die Straße.


Anmelden zum Antworten