Odpowiedz

11

NVI jest idiomem, metoda szablonowa to wzorzec. NVI jest implementacją Wzoru metody szablonu przy użyciu dynamicznej wysyłki w C++; możliwe jest również tworzenie metod szablonów w C++ za pomocą metaprogramowania szablonów w celu wyeliminowania dynamicznej wysyłki.

Wzór jest bardziej ogólny niż idiom, a języki mogą używać różnych idiomów do implementacji wzorca.

+1

Mówisz więc, że NVI jest po prostu specyficzną dla języka implementacją wzorca metody szablonów i poza tym, że nie ma prawdziwej różnicy? W jaki sposób używałbyś szablonów C++ do osiągnięcia tego samego rezultatu? –

+0

@Robert S. Barnes O ile widzę, nie ma oczywistego sposobu używania szablonów C++ jako metody szablonu. Metoda szablonowa mówi: "rób to, a następnie wykonaj inną rzecz", a podczas gdy możesz stworzyć funktora dla jednej lub drugiej rzeczy do zrobienia, nie ma prawdziwej relacji z parametrami, które daje szablon C++. –

+0

Sądzę, że nadal nie rozumiem, o co ci chodziło: "możliwe jest również tworzenie metod szablonów w C++ za pomocą metaprogramowania szablonów w celu wyeliminowania dynamicznej wysyłki." –

8

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)
+0

+1 dla "wirtualnego jest szczegółów implementacji" i rozważenia ABI. – neuro

+0

+1. Jednak nie zgadzam się z tobą na temat tworzenia oddzielnych klas podobnych do pimpl dla wirtualnego interfejsu. Najbardziej oczywistym minusem jest to, że podwaja liczbę wymaganych klas. Także jeśli FooImpl nie był nieprzezroczysty, może to skłonić ludzi do użycia go bezpośrednio i pominięcia Foo. Jest jednak mało prawdopodobne, aby był nieprzejrzysty, ponieważ celem NVI jest umożliwienie ludziom obejścia wirtualnych implementacji, więc FooImpl musiałby być publicznie dostępny. Mimo, że ma on swoje wady, myślę, że wdrożenie NVI w jednej klasie i unikanie publicznych wirtualnych funkcji razem jest lepszym podejściem. – stinky472

+0

To także łatwiejsza do wymuszenia polityka: brak publicznych funkcji wirtualnych w przeciwieństwie do tych, które tworzą specjalne przypadki dla klas podobnych do pimpl, które udostępniają publiczny wirtualny interfejs. – stinky472