Jak już powiedziano, NVI jest idiomem programistycznym, związanym z kategorią języków. To był promowany przez Herb Sutter między innymi dlatego, że pomaga kontrakty enforcing:
- klasy niezmienników
- kontrakty funkcyjne (twierdzenia ponad parametrów przekazywanych i zwracanej wartości generowanego)
- powtarzalnych czynności (takich jak rejestrowanie)
- kontrola nad wyjątków generowanych (zły pomysł, chociaż;))
Jednakże realizacja może w rzeczywistości różnią się znacznie, na przykład kolejny przykład nvi realizacja jest łączenie go z Pimpl:
class FooImpl;
class Foo
{
public:
enum type { Type1, Type2 };
Foo(type t, int i, int j);
int GetResult() const;
private:
FooImpl* mImpl;
};
A za realizację:
struct FooImpl
{
virtual ~FooImpl();
virtual int GetResult() const;
};
class FooType1: public FooImpl
{
public:
FooType1(int i, int j);
virtual int GetResult() const;
private:
/// ...
};
Zawsze jest on przenoszony punkt lepiej. Wymyśliłeś to?
Najważniejsze jest to, że virtual
jest szczegółem implementacji. Ujawnienie szczegółów implementacji w interfejsie jest złym pomysłem, ponieważ możesz chcieć je zmienić.
Ponadto szczegóły implementacji mają tendencję do bałaganu z kompatybilnością binarną. Na przykład dodanie nowej metody w klasie może zmienić układ wirtualnego stołu (powszechna technika implementacji), a tym samym spartaczyć zgodność binarną. W gcc musisz upewnić się, że dodasz go jako ostatni (spośród wirtualnych), jeśli chcesz zachować kompatybilność.
Stosując powyższą kombinację NVI + Pimpl, nie ma w ogóle żadnej (nawet prywatnej) klasy narażonej. Układ pamięci jest zgodny wstecz i do przodu. Osiągnęliśmy zgodność binarną.
Tu używamy kilka wzorów na raz:
- Template Method
- Strategię (ponieważ możemy zamienić wskaźnik do woli)
- Factory (do decydowania, których realizacja mamy)
Nie użyłbym linku wikipedii jako odnośnika, ich użycie zamków jest kruche w obliczu wyjątków ... –