5 Stimmen

Mehrfachvererbung und Datenelemente

Ich habe nie mit Mehrfachvererbung gearbeitet und bin über ein Designproblem gestolpert, mit dem ich nie konfrontiert war.

class A {
     //..methods..
}

class B : public A {
    int b;
    //...methods..
}

class C : public A {
    int c1,c2;
}

class D : public B,public C {
}

Hier ist der klassische Diamant. Die Tatsache ist, dass C ist eigentlich nur ein A mit zwei Extra-Int. und D ist eigentlich nur eine Ansammlung von B y C aber ich habe das Gefühl, dass die Mehrfachvererbung nicht dazu gedacht ist, solche Dinge zu machen. Oder, dass es andere beste Praktiken, dies zu tun sein kann.

Der Grund, warum im versuchen, Mehrfachvererbung zu implementieren ist, dass ich eine Funktion wie schreiben möchten void func(A*) und übergeben Sie ihm entweder eine A o D Klassenzeiger. Mein naiver Versuch besteht darin, einen einfachen Cast durchzuführen:

void func(A* a) { // <-- I call this with a A or D pointer
   // ..do something with A members..

    if(is_the_case) { // <-- Im sure the passed "a" pointer is actually a *D
        D* d = (D*)a;
       // ..do something with the extra 2 ints provided by the C class..
    } 
}

Funktioniert nicht Kompiliert gut, aber ich habe ein wirklich seltsames Verhalten, wenn if(is_the_case) ausgeführt wird, wodurch 2 zusätzliche Ints gelöscht werden c1 y c2 , klärt auch b (vererbt von B ).

Ich erinnerte mich an das Problem mit den Diamanten, aber hier gibt es nur eines B (und 2 A ) in der Hierarchie, daher verstehe ich nicht, warum b wird ebenfalls geräumt. Nur um es zu versuchen, habe ich public virtual in B y C Erklärung. Jetzt ist jeder Cast ein Kompilierfehler, es sei denn, ich verwende eine dynamic_cast ..

Kann mir jemand erklären, was hinter den Kulissen passiert? Was ist die beste Vorgehensweise, wenn man bedenkt, dass es noch andere Klassen gibt wie:

class E : public A {
    int e;
    //..methods..
}

class F : public E,public C {
}

Das heißt, andere Klassen, die nur eine Aggregation einer Klasse sind, die von A + zwei zusätzliche Ints, geerbt von C und das kann an eine Funktion übergeben werden, die *A

Danke, ich habe mein Bestes getan, um mich so klar wie möglich auszudrücken.

4voto

Puppy Punkte 141483

Ihr Code funktioniert, weil Sie einen Cast im C-Stil verwendet haben, der ein reinterpret_cast und soweit ich weiß, können Sie reinterpret_cast zwischen zwei beliebigen Zeigertypen, auch wenn es keinen Sinn ergibt. Sie müssen einen dynamic_cast beim Casting von einer mehrfach vererbten Basisklasse zu einer abgeleiteten Klasse. A static_cast würde zu einem Kompilierfehler führen. In der Tat, dynamic_cast erledigt beide Aufgaben auf einmal.

void func(A* a) { // <-- I call this with a A or D pointer
   // ..do something with A members..

    if(D* d = dynamic_cast<D*>(a)) { // a definitely points to a D
        // and we got a guaranteed good pointer too
    } 
}

Dies ist ein hervorragendes Beispiel dafür, warum C-Style Casts vermieden werden sollten.

1voto

BЈовић Punkte 59375

Sie können über Mehrfachvererbung lesen aquí .

Was Sie tun müssen, ist, die nächsten Klassen virtuell zu erben:

class B : virtual public A {
    int b;
    //...methods..
};

class C : virtual public A {
    int c1,c2;
};

1voto

fizzer Punkte 13343

Ich weiß nicht, warum dieses Verhalten bei Ihnen auftritt. Wie in den Kommentaren erwähnt, sollte Ihr Code wie beschrieben nicht kompiliert werden.

Gibt es ein Designproblem? Ja. Treffen Sie keine expliziten Entscheidungen auf der Grundlage der Abfrage eines Objekttyps. Dafür sind virtuelle Funktionen da.

class A
{
public:
    virtual void prepare_for_list_insertion() {}
};

class B : public A
{
    int b;
};

class C : public A
{
private:
    int c1, c2;

protected:
    void clear() { c1 = c2 = 0; }
};

class D : public B, public C
{
public:
    void prepare_for_list_insertion()
    {
        clear();
    }
};

void func(A* a)
{
    a->prepare_for_list_insertion();
}

int main()
{
    A a;
    func(&a); // calls A::prepare_for_list_insertion
    D d;
    // You need a cast to disambiguate the A base - either will do.
    func(static_cast<C*>(&d)); // calls D::prepare_for_list_insertion
    func(static_cast<B*>(&d)); // calls D::prepare_for_list_insertion
}

CodeJaeger.com

CodeJaeger ist eine Gemeinschaft für Programmierer, die täglich Hilfe erhalten..
Wir haben viele Inhalte, und Sie können auch Ihre eigenen Fragen stellen oder die Fragen anderer Leute lösen.

Powered by:

X