Wywołanie wirtualnej funkcji w konstruktorze bazowym

0

Witam, Czy możecie mi powidzieć dlaczego nie działa mi wywołanie funkcji wirtualnej w konstruktorze w klasie bazowej, która to jest zdefiniowana w klasie pochodnej? Jeżeli klasa ma funkcje wirtualną to wywołuje implementacje klasy bazowej a gdy to jest funkcja czysto wirtualna to jest błąd kompilacji.

Oto kodzik:

#include <cstdio>

class CBazowa {
public:
	CBazowa() {
		this->move();
	}
	void virtual move() = 0;

};

class CPochodna: public CBazowa {
public:
	virtual void move() override {
		printf("Pushdown");
	}
};


int main()
{
	CPochodna * pBase = new CPochodna();
    delete pBase;
    return 0;
}


 
1

As designed.
https://isocpp.org/wiki/faq/strange-inheritance#calling-virtuals-from-ctors

W wielkim skrócie: obiekty są tworzone "od dołu", tzn kiedy działa konstruktor twojej klasy bazowej to "gotowe" są tylko elementy do tego poziomu dziedziczenia. W szczególności nie są gotowe żadne pola pochodzące z klasy pochodnej. Metoda wirtualna mogłaby z takich pól próbować korzystać co skończyłoby się tragicznie. Co wiecej taki błąd byłby trudny do znalezienia bo aplikacja kompilowałaby się ok a wysypywała w runtime.

Zmodyfikujmy lekko twój kod:

#include <cstdio>
 
class CBazowa {
public:
    CBazowa() {
        this->move();
    }
    void virtual move() = 0;
};
 
class CPochodna: public CBazowa {
private:
    int position;
public:
    virtual void move() override {
        position = 1;
    }
};
 
int main()
{
    CPochodna * pBase = new CPochodna();
    delete pBase;
    return 0;
}
 

Widzisz teraz problem? W chwili działania konstrutkora CBazowa pola position jeszcze w ogóle nie ma...

0

Tak, dzieki. Jestem programistą Javy a tam nie ma problemu z taka konstrukcja więc myślałem że funkcje czysto wirtualne to to samo co:
public abstract void move(); Chciałem tym osiągnąć uproszeczenie konfiguracji obiektu, wywolując potrzebne metody w konstruktorze bazowym i nadpisywac metody kluczowe w pochodnej.

3

To się załatwia za pomocą fabryki:

/*static*/ CBazowa *CBazowa::createInstance(jakisParametr) {
    CBazowa *wynik = NULL:
    if (jakisParametr) {
         wynik = new CPochodna();
         wynik->move();
    }
    return wynik;
}
0

Masz racje można i tak. :) Fabryka to "ładne" rozwiązanie.

5

Konstruktor w C++ powinien robić jak najmniej: zaincjować pola, może zaalokować jakąś pamięć.
Wszystko ponad to powinno być przeniesione do metody.

0

Z tego co pamiętam wskaźnik w vtable nie jest jeszcze przesunięty w trakcie wywoływania konstruktora. Także mimo, że technicznie jest to możliwe i z rzadka nawet Ci się nie wywali, to dobrą praktyką jest nie wołać żadnych wirtualnych metod w konstruktorze.

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