[solved]Dziedziczenie wirtualne i parametry w konstruktorach

0

Mam taką hierarchię klas:

class A
{
    char a;

    public:
        A(char _a)
        {
            a = _a;
        }
};

class B : public A
{
    char b;
    public:
        B(char _a, char _b) : A(_a)
        {
            b = _b;
        }
};

class C : public B
{
    char c;
    public:
        C(char _a, char _b, char _c) : B(_a, _b)
        {
            c = _c;
        }
};

B dziedziczy po A, C dziedziczy po B. Wszystko jest w porządku. Problem pojawia się, gdy B dziedziczy po A wirtualnie:

class A
{
    char a;

    public:
        A(char _a)
        {
            a = _a;
        }
};

class B : virtual public A
{
    char b;
    public:
        B(char _a, char _b) : A(_a)
        {
            b = _b;
        }
};

class C : public B
{
    char c;
    public:
        C(char _a, char _b, char _c) : B(_a, _b)
        {
            c = _c;
        }
};

Wówczas kompilator zgłasza błąd, że w klasie A nie ma bezparametrowego konstruktora. O co chodzi? Zauważyłem, że jeśli klasę zmodyfikuję w taki sposób:

class C : public B
{
    char c;
    public:
        C(char _a, char _b, char _c) : A(_a), B(_a, _b)
        {
            c = _c;
        }
};

To program się kompiluje. Ale chyba nie tak to się rozwiązuje, prawda? Struktura mojego programu jest znacznie bardziej złożona, więc gdybym zawsze wywoływał konkretne konstruktory wszystkich klas bazowych, po kod stałby się "trochę" bardziej zagmatwany.

Jak to rozwiązać?

0
  1. ten przykład nie nadaje się na dziedziczenie wirtualne. Dziedziczenie wirtualne ma sens jedynie gdy masz dziedziczenie wielokrotne, po klasach, które mają wspólnego przodka. Najprostszy przykład to:
class A{ ... };
class B1 : virtual public A { ... };
class B2 : virtual public A { ... };
class D : public B1, public B2 { ... };

To zapobiega powstawaniu wielu pól z klasy A w klasie D.
Wiec, żeby jedna kopia pól z klasy A, funkcjonowała prawidłowo, to konstruktor klasy powinien być wywołany tylko raz, a nie dwa raz z klasy B1 i drugi B2. Jeśli to zrozumiesz, to staje się jasne czemu kompilator się buntuje tak jak to opisałeś.

0

Jasne, jasne, tak jest w moim programie, tu tylko podałem to jako przykład. Mnie jednak zastanawia jak w takim razie to rozwiązać? W każdej kolejnej klasie wywoływać jednoparametrowy konstruktor klasy A?

0

przecież podałeś rozwiązanie w ostatnim kodzie!

0

Tak, tak, tylko mnie to nie satysfakcjonowało i zastanawiałem się po prostu czy prawidłowo robi się to inaczej.

Już zacząłem pisać dokładniej o swoim problemie, ale właśnie przeczytałem fragment z Grębosza i wszystko się wyjaśniło (choć nie powiem, trochę się zdziwiłem). Jeśli ktoś jeszcze tu trafi mając podobny problem, oto cytat:

"W klasie najbardziej pochodnej musimy zadbać o uruchomienie konstruktora klasy wirtualnej A. (...) W wypadku dziedziczenia wirtualnego w konstruktorze klasy najbardziej pochodnej odpowiadamy za uruchomienie konstruktora klasy wirtualnej (...). Mimo, że wywołania konstruktora klasy wirtualnej będą rozsiane po całej hierarchii - kompilator konstruując obiekt klasy D (chodzi o najbardziej odległą klasę - przyp. cytującego) weźmie pod uwagę tylko to wywołanie z klasy najbardziej pochodnej - czyli klasy D. Wszystkie inne wywołania znajdujące się na listach inicjalizacyjnych konstruktorów klas podstawowych (rodziców, dziadków, itd.) - zostaną zignorowane. Jeśli na liście inicjalizacyjnej konstruktora klasy najbardziej pochodnej nie ma wywołania konstruktora klasy wirtualnej, wówczas uruchamiany jest jej konstruktor domniemany. Oczywiście jeśli klasa wirtualna nie ma żadnego konstruktora - to problemu w ogóle nie ma. (...) Gdyby konstruktora domniemanego nie było - a klasa miała inne konstruktory - to w wypadku odziedziczenia wirtualnego takiej klasy, każdy konstruktor klasy pochodnej do tysiącznego pokolenia włącznie musiałby zadbać o uruchomienie konstruktora klasy wirtualnej."

Już wszystko jasne! ;)

1 użytkowników online, w tym zalogowanych: 0, gości: 1