To może przyjść trochę późno po innych odpowiedzi, ale będę jeszcze spróbować.
Ty może zaimplementować to bezpiecznie i bez zmiany klas pochodnych. Należy jednak zmienić użyć wszystkich tych klas, które mogą być znacznie gorsze, w zależności od scenariusza. Jeśli nadal projektujesz, może to być opłacalna alternatywa.
Zasadniczo można zastosować curiously recurring template pattern i wprowadzić kod inicjalizacyjny po wywołaniu konstruktora. Co więcej, jeśli zrobisz to tak, jak to napisałem poniżej, możesz nawet ochronić load
przed wywołaniem dwa razy.
struct Loader {
int get(int index) { return 0; }
};
struct Base {
virtual ~Base() {} // Note: don't forget this.
protected:
virtual void load(Loader &) = 0;
};
struct Derived : public Base {
int value;
protected:
void load(Loader &l) {
value = l.get(0);
}
};
template<typename T>
class Loaded : public T
{
public:
Loaded() {
Loader l; T::load(l);
}
};
int main (int, char **)
{
Loaded<Derived> derived;
}
Szczerze mówiąc, rozważałbym alternatywny projekt, jeśli potrafisz. Przenieść kod z load
do swoich konstruktorów i zapewnić ładowarka jako argument odniesienia zalegających w następujący sposób:
struct Derived : public Base {
Derived (Loader& loader = Loader()) { ... }
};
ten sposób całkowicie uniknąć tego problemu.
Podsumowanie: do wyboru są następujące:
- Jeśli nie są ograniczone przez ograniczenia zewnętrznych i nie mają obszerną bazę kodu w zależności od tego, zmienić swój projekt na coś bezpieczniejszego.
- Jeśli chcesz zachować
load
sposób, w jaki jest i nie zmieniasz zbyt wiele zajęć, ale jesteś skłonny zapłacić cenę za zmianę wszystkich wystąpień, zastosuj CRTP, jak zaproponowano powyżej.
- Jeśli chcesz zachować zgodność z istniejącym kodem klienta, musisz zmienić swoje zajęcia, aby korzystać z PIMPL, ponieważ inni sugerują lub żyją z istniejącym problemem.
Na czym polega problem? Możesz wywołać metodę czysto wirtualną. –
@Benoit: Nie w konstruktorze. @Vargas: Prawdopodobnie można go zaprojektować lepiej, więc nie masz tej zależności. Na przykład, dlaczego 'load' jest osobną funkcją, która jest wywoływana w konstruktorze? Dlaczego nie pozwolić, aby 'Derived' wczytał własne wartości. – GManNickG
@Benoit: Od konstruktora? !!! To się nazywa niezdefiniowane zachowanie w C++ –