problem mit new-array und klasse



  • guten abend

    ich habe ein problem, dass wenn ich eine klasse definiere

    class demo{
    private: const int zahl;
    public: demo(const int newZahl):zahl(newzahl) {}
    };
    

    und dann versuche, ein array mit new zu bilden

    demo *p;
    p= new demo [5];
    

    .
    der compiler beklagt sich, dass ihm ein passender konstruktor fehlt, nur kann ich keinen anderen bilden, denn dieser schreibt mir andere, falsche werte für zahl rein (zb

    demo():zahl(0) {}
    

    )
    und afaik ist das "demo" in "new demo [5]" nur dazu da, um anzuzeigen vie groß die klasse ist

    jedenfalls muss ich dannach in einer schleife noch auf die elemente des arrays zugreifen können:

    for(int i=0; i<=5; i++)
    {
    p[i]=new demo((const int)i);
    }
    

    das geht nichtmehr, wenn ich enen konstruktor erzeuge, wie im text oben beschrieben.
    hat jemand eine lösung?

    vielen dank euch allen

    mfg
    hann



  • wenn du nicht auf ekelhafte sachen wie placement-new zurückgreifen willst (und du willst nicht), dann verwende std::vector.

    #include <vector>
    
    //...
    vector<demo> v(10, demo(42));
    

    zugriff wie gewohnt über den indexoperator.

    hann schrieb:

    und afaik ist das "demo" in "new demo [5]" nur dazu da, um anzuzeigen vie groß die klasse ist

    das verstehe ich nicht.

    jedenfalls muss ich dannach in einer schleife noch auf die elemente des arrays zugreifen können:

    for(int i=0; i<=5; i++)
    {
    p[i]=new demo((const int)i);
    }
    

    das geht nichtmehr, wenn ich enen konstruktor erzeuge, wie im text oben beschrieben.
    hat jemand eine lösung?

    das geht nicht nur "nichtmehr", das ging nie. btw. ein const_cast ist hier nicht nötig und das argument im konstruktor muss auch kein const int sein, int reicht. (im der elementvariable in der klasse wird einfach eine kopie davon angelegt)

    aber ich weiß hier auch nicht genau, was du bezwecken willst. wenn du schon objekte angelegt hast, warum willst du dann nochmal ein neues anlegen?



  • Für

    p= new demo [5];
    

    brauchst du einen Standardkonstruktor. Zusätzlich kommt bei dir das Problem hinzu, dass const-Member in der Memberinitialisierungsliste initialisiert werden müssen.

    Das Problem kann man auf vielen Wegen umgehen. Ohne Standardkonstruktor musst du eben alles selbst machen. Zum Beispiel so:

    demo *p;
    
    p = (demo*) new char[sizeof(demo) * 5]; //Speicher reservieren
    
    for (int i=0; i  < 5; ++i)
        new (p + i) demo(i);                //Für jedes Objekt den Konstruktor aufrufen
    
    // Array verwenden
    
    for (int i=0; i< 5; ++i)
        p[i].demo::~demo();                 //Für jedes Objekt den Destruktor aufrufen
    
    delete[] (char*) p;                     //Speicher freigeben
    

    Du kannst natürlich auch alles in statischen Methoden der Klasse verstecken.

    Alternativ kannst du STL-Container (Vektor, ...) verwenden.



  • Hallo hann,

    zunächst stimme ich @queer_boy mit der Verwendung der Klasse Vector aus der Standard-Bibliothek zu. Die erspart dir viel Ärger um das Speichermanagement.

    Was @Zweifler da bezüglich der Speicheranforderung über den Umweg mit dem Typ 'char' geschrieben hat sehe ich als unsauber an.
    Für seine C-Casts gibt es als C++-Entsprechung den reinterpret_cast< ... >.

    Zunächst aber zu Deinem Code:

    p= new demo [5];
    

    Diese Verwendung des new-Operators reserviert Platz für 5 Objekte des Typs 'demo' und initialisiert alle 5 Objekte mit dem Standardkonstruktor. Da dieser in Deiner Klasse nicht vorhanden ist, beschwert sich der Compiler.

    Bei der von Dir verwendeten Version des 'operator new' zur Konstruktion der Objekte innerhalb der Schleife mit

    p[i]=new demo((const int)i);
    

    legst Du keine Objekte im bereits reservierten Speicher an, sondern forderst für jedes Objekt wieder neuen Speicher an, in welchem dann das Objekt konstruiert wird - auch nicht das, was Du möchtest.

    Für die Reservierung von uninitialisiertem Rohspeicher mußt du die folgende Version des 'operators new' verwenden:

    p = static_cast<demo*>(operator new[] (sizeof(demo)*6));
    

    Dieser Operator muß explizit aufgerufen werden und erhält die Größe des zu reservierenden Bereiches in Byte. Der Rückgabewert dieses operator new ist immer vom Typ 'void*', weshalb hier der C++-Cast nach 'demo*' eingesetzt wurde.

    Die eigentliche Konstruktion der Objekte in dem bereits angeforderten Speicher erfolgt dann mit dem 'placement new', welchem im ersten Parameter die Speicheradresse und im zweiten der Konstruktor für die Objekt-Konstruktion übergeben wird. Das sieht dann so aus, wie @Zweifler es zeigt oder unter Verwendung des Index-Operators:

    new (&p[i]) demo(i);
    

    Der zum oben gezeigten 'operator new' passende 'operator delete' sieht folgendermaßen aus:

    operator delete[] (p);
    

    Bei dieser Art der Programmierung kommt es jedoch sehr schnell zu fehlerhaftem Speicherzugriff. Bei Deinem geposteten Entwurf zum Beispiel reservierst Du Platz für 5 Objekte, beschreibst später in der Schleife jedoch 6 Objekte, überschreitest also den dir bereitgestellten Speicherplatz.
    Von daher kann ich nur die Empfehlung der Nutzung der Vector-Klasse aus der Standard-Bibliothek wiederholen.

    Edit: Für die Art und Weise, wie ich auf @Zweiflers post in der Erstfassung dieses Beitrags reagiert habe, möchte ich mich entschuldigen. In der jetzigen Version habe ich die entsprechenden Stellen geändert.



  • mario_69 schrieb:

    Was @Zweifler da geschrieben hat, vergiß ganz schnell. Der Umweg über 'char' bei dem Speichermanagement ist ein böser C-Hack, und für den C-Cast gibt es auch eine saubere Entsprechung in C++.

    Das ist zwar kein C-Hack und schon gar kein böser, aber ich gebe dir natürlich Recht, dass man mit den opretor new und operator delete-Funktionen günstiger Möglichkeiten hat Roh-Speicher zu verarbeiten.

    Über das static_cast kann man sich streiten. Ich finde es sieht an der Stelle einfach nur scheiße aus und macht den Quelltext unleserlich.

    mario_69 schrieb:

    Zu beachten ist hier das Konstrukt '&p[i]'. Obwohl p ein Zeiger auf ein Objekt vom Typ 'demo' ist, ist p[i] direkt ein Objekt vom Typ 'demo'! Deswegen der Adressoperator '&'

    Das "Konstrukt" ist ja richtig süß. Es steht halt schlicht &(*(p+i)) da. Also doppeltgemoppelter Käse.

    Ansonste Danke, dass du dir die Mühe gemacht hast dasselbe nochmal in Grün hinzuschreiben. 😉



  • Der static_cast hat nichts mit schön oder unschön zu tun. Vielmehr drücke ich damit aus, daß ich eine Konvertierung zwischen verwandten Typen durchführe. Und dies wird in diesem Fall durch den Compiler während der Übersetzung geprüft.

    Der einfache C-Cast ist in jedem Fall schwerer im Quellcode zu finden, und er konvertiert nicht nur zwischen verwandten Typen sondern auch zwischen konstanten und nicht konstanten Typen sowie zwischen völlig unterschiedlichen Typen. Ich bin der Meinung, daß der Programmierer seine Absichten möglichst deutlich machen soll, und die 4 C++-Casts beschreiben sehr genau die Absichten.



  • Klar, aber bei einem void* ist das in meinen Augen nur Ballast. Und die relevante Information steht auch andersherum bereits da.


Anmelden zum Antworten