As already mentioned, NVI is a prosaic idiom associated with the category of languages. Herb Sutter was among them because he helps conclude contracts:
- class invariants
- function contracts (statements on the passed parameters and return value)
- repetitive operations (e.g. logging)
- control over the generated exceptions (a bad idea, though;))
However, the implementation may vary significantly, for example, another example of an NVI implementation is combining it with Pimpl:
class FooImpl; class Foo { public: enum type { Type1, Type2 }; Foo(type t, int i, int j); int GetResult() const; private: FooImpl* mImpl; };
And for implementation:
struct FooImpl { virtual ~FooImpl(); virtual int GetResult() const; }; class FooType1: public FooImpl { public: FooType1(int i, int j); virtual int GetResult() const; private:
I always found that this conveyed the point better. Do you get it?
The main thing is that virtual is an implementation detail. And exposing implementation details in an interface is a bad idea because you can change them.
In addition, implementation details are typically related to binary compatibility. For example, adding a new virtual method to a class can change the layout of a virtual table (a common implementation technique) and thus enable binary compatibility. On gcc, you need to make sure that you add it last (among the virtual ones) if you want to keep compatibility.
Using the above NVI + Pimpl combination, there is no virtual (not even closed) in the open class. Memory layout compatible with feedback. We have achieved binary compatibility.
Here we use several templates at once:
- Template method
- Strategy (since we can change the pointer as desired)
- Factory (to decide which implementation we will get)
Matthieu M.
source share