множественное наследование и члены данных - PullRequest
5 голосов
/ 25 июня 2011

Я никогда не использовал множественное наследование и столкнулся с проблемой проектирования, с которой я никогда не сталкивался ..

class A {
     //..methods..
}

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

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

class D : public B,public C {
}

Вот классический бриллиант. Дело в том, что C на самом деле просто A с двумя дополнительными целыми числами. и D на самом деле представляет собой совокупность B и C, но я чувствую, что множественное наследование не было разработано для создания подобных вещей. Или что для этого могут быть другие лучшие практики.

Причина, по которой я пытаюсь реализовать множественное наследование, заключается в том, что я хочу написать функцию типа void func(A*) и передать ей либо указатель класса A, либо D. Моя изобретательная попытка - сделать простое приведение:

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..
    } 
}

Не работает .. Компилируется нормально, но у меня действительно странное поведение при выполнении if(is_the_case), очищает эти 2 дополнительные целые числа c1 и c2, очищает также b (наследуется B ).

Я вспомнил о проблеме алмазов, но здесь есть только один B (и 2 A) в иерархии, поэтому я не понимаю, почему очищается также b. Просто чтобы попробовать, я использовал public virtual в B и C декларации. Теперь каждое приведение является ошибкой компиляции, если я не использую dynamic_cast ..

Может кто-то прояснить, что происходит за кулисами? Как лучше всего это делать, учитывая, что есть и другие классы, такие как:

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

class F : public E,public C {
}

То есть другие классы, которые являются просто совокупностью классов, производных от A + двух дополнительных целочисленных значений, унаследованных от C, и которые могут быть переданы в функцию, которая принимает *A

Спасибо, я сделал все возможное, чтобы быть как можно яснее ..

Ответы [ 3 ]

4 голосов
/ 25 июня 2011

Ваш код работает, потому что вы использовали приведение в стиле C, которое может быть reinterpret_cast, и я понимаю, что вы можете reinterpret_cast между любыми двумя типами указателей, даже если это не имеет смысла.Вы должны использовать dynamic_cast при приведении из многократно унаследованной базы к более производному классу.static_cast приведет к ошибке времени компиляции.Фактически, dynamic_cast выполняет обе работы одновременно.

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
    } 
}

Это отличный пример того, почему следует избегать приведения в стиле C.

1 голос
/ 25 июня 2011

Я не знаю, почему вы видите это поведение. Как отмечено в комментариях, ваш код, как описано, не должен компилироваться.

Есть ли проблемы с дизайном? Да. Не делайте явных выборов, основываясь на запросах типа объекта. Вот для чего нужны виртуальные функции.

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
}
1 голос
/ 25 июня 2011

Вы можете прочитать о множественном наследовании здесь .

Что вам нужно сделать, это фактически наследовать следующие классы:

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

class C : virtual public A {
    int c1,c2;
};
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...