Frage

Wenn ich ein einzelnes Objekt zuweisen, funktioniert dieser Code in Ordnung. Wenn ich versuche, die Array-Syntax hinzuzufügen, es Segfaults. Warum ist das? Mein Ziel hier ist von der Außenwelt, die Tatsache zu verbergen, dass Klasse c mit b Objekten intern. Ich habe das Programm gebucht Codepad für Sie zu spielen.

#include <iostream>

using namespace std;

// file 1

class a
{
    public:
        virtual void m() { }
        virtual ~a() { }
};

// file 2

class b : public a
{
    int x;

    public:
        void m() { cout << "b!\n"; }
};

// file 3

class c : public a
{
    a *s;

    public:
        // PROBLEMATIC SECTION
        c() { s = new b[10]; } // s = new b;
        void m() { for(int i = 0; i < 10; i++) s[i].m(); } // s->m();
        ~c() { delete[] s; } // delete s;
        // END PROBLEMATIC SECTION
};

// file 4

int main(void)
{
    c o;

    o.m();

    return 0;
}
War es hilfreich?

Lösung

Ein Problem ist, dass der Ausdruck s[i] Zeigerarithmetik verwendet die Adresse des gewünschten Objekts zu berechnen. Da s als Zeiger definiert wird a, ist das Ergebnis korrekt für eine Anordnung von as und inkorrekten für eine Anordnung von bs. Die Dynamik, die durch Vererbung zur Verfügung gestellt Bindung funktioniert nur für Methoden, nichts anderes (zum Beispiel keine virtuellen Datenelemente, keine virtuelle sizeof). So beim Aufruf der Methode s[i].m() der this Zeiger auf wird festgelegt, was der ith a Objekt im Array sein würde. Da aber in Wirklichkeit die Anordnung einen bs ist, endet es nach oben (manchmal), der auf irgendwo in der Mitte eines Objekts und Sie erhalten eine segfault (wahrscheinlich, wenn das Programm versucht, auf das VTable Objekt zuzugreifen). Sie könnten in der Lage, das Problem durch die Virtualisierung und Überlastung operator[]() zu korrigieren. (Ich habe es nicht durchdenken, um zu sehen, ob es tatsächlich funktionieren, though.)

Ein weiteres Problem ist die delete im destructor, aus ähnlichen Gründen. Sie könnten in der Lage sein, es zu virtualisieren und überlastet. (Wieder nur ein zufälliger Gedanke, der in meinem Kopf auftauchte. Könnte nicht funktionieren.)

Natürlich, Gießen (wie von anderen vorgeschlagen) wird arbeiten.

Andere Tipps

ein Feld von 10 b die mit new erstellen und dann zu einem a* seiner Adresse zuweisen ist nur Ärger bringen.

Behandeln Sie nicht Arrays polymorph.

Weitere Informationen finden Sie unter 06. Arrays und die STL (ARR) der CERT C ++ Coding Standard sichern.

Sie haben ein Array vom Typ „b“ nicht vom Typ „a“ und Sie es auf einen Zeiger vom Typ eines zuweisen. Polymorphismus übertragen sich nicht auf dynamische Arrays.

 a* s

a

 b* s

und Sie werden sehen, dieser Start zu arbeiten.

Nur noch nicht gebunden Zeiger kann polymorph behandelt werden. Denken Sie darüber nach

 a* s = new B(); // works
 //a* is a holder for an address

 a* s = new B[10]
 //a* is a holder for an address
 //at that address are a contiguos block of 10 B objects like so
 // [B0][B2]...[B10] (memory layout)

, wenn Sie über das Array mit s iterieren, darüber nachzudenken, was verwendet wird,

 s[i]
 //s[i] uses the ith B object from memory. Its of type B. It has no polymorphism. 
 // Thats why you use the . notation to call m() not the -> notation

, bevor Sie in ein Array umgewandelt Sie hatte gerade

 a* s = new B();
 s->m();

s hier ist nur eine Adresse, die kein statisches Objekt wie s [i]. Die Adresse der immer noch dynamisch gebunden werden kann. Was ist bei s? Wer weiß? Etwas an einer Adresse s.

Siehe Ari 's große Antwort unten für weitere Informationen darüber, warum dies auch nicht Sinn in Bezug darauf, wie C-Stil-Arrays machen wird verlegt werden.

Jede Instanz von B enthält sowohl X Datenelement und den „vptr“ (Zeiger auf die virtuelle Tabelle).

Jede Instanz von A enthält nur die "vptr"

So sizeof (a)! = Sizeof (b).

Nun, wenn Sie dies tun: „S = new b [10]“ Sie lag auf dem Speicher 10 Fälle von b in einem rohen, S (die den Typ eines * hat) wird immer am Anfang, dass rohe Daten .

in C :: m () -Methode, sagen Sie den Compiler eine Reihe von "a" iterieren (da s den Typ eines * hat), ABER , s ist actualy zeigt auf ein Array von "b". Also, wenn Sie anrufen s [i], was der Compiler actualy tun ist „s + i * sizeof (a)“, der Compiler springt in Einheiten von „a“ statt Einheiten von „b“ und da a und b nicht hat die gleiche Größe haben, können Sie eine Menge mambojumbo erhalten.

Ich habe eine Abhilfe auf Ihre Antworten basiert herausgefunden. Es erlaubt mir, die Details der Implementierung unter Verwendung einer Schicht von Dereferenzierung zu verstecken. Es erlaubt mir auch zu mischen und anzupassen Objekte in meinem Array. Dank!

#include <iostream>

using namespace std;

// file 1

class a
{
    public:
        virtual void m() { }
        virtual ~a() { }
};

// file 2

class b : public a
{
    int x;

    public:
        void m() { cout << "b!\n"; }
};

// file 3

class c : public a
{
    a **s;

    public:
        // PROBLEMATIC SECTION
        c() { s = new a* [10]; for(int i = 0; i < 10; i++) s[i] = new b(); }
        void m() { for(int i = 0; i < 10; i++) s[i]->m(); }
        ~c() { for(int i = 0; i < 10; i++) delete s[i]; delete[] s; }
        // END PROBLEMATIC SECTION
};

// file 4

int main(void)
{
    c o;

    o.m();

    return 0;
}
Lizenziert unter: CC-BY-SA mit Zuschreibung
Nicht verbunden mit StackOverflow
scroll top