How to implement an interface class using the non-virtual interface idiom in C++?

前端 未结 4 1655
猫巷女王i
猫巷女王i 2020-12-19 15:10

In C++ an interface can be implemented by a class whose methods are pure virtual.

Such a class could be part of a library to describe what methods an object should

相关标签:
4条回答
  • 2020-12-19 15:50

    Commonly, the reason for using the NVI (sometimes also called "Template Method") is that derived classes should only change a part of the base class' behavior. So what you do is this:

    class base {
      public:
        void f()
        {
          // do something derived classes shouldn't interfere with          
          vf();
          // do something derived classes shouldn't interfere with          
          vg();
          // do something derived classes shouldn't interfere with          
          vh();
          // do something derived classes shouldn't interfere with          
        }
      private:
        virtual void vf(); // might be pure virtual, too
        virtual void vg(); // might be pure virtual, too
        virtual void vh(); // might be pure virtual, too
    };
    

    Derived classes can then plug into f() at the spots they are meant to and change aspects of f()'s behavior, without messing up its fundamental algorithm.

    0 讨论(0)
  • 2020-12-19 15:57

    I think you've got your NVI pattern around the wrong way: http://en.wikibooks.org/wiki/More_C%2B%2B_Idioms/Non-Virtual_Interface

    Not sure if that solves your issue though.

    class IFoo
    {
        public:
           void method() { methodImpl(); }
        private:
           virtual void methodImpl()=0;
    };
    
    class FooBase : public IFoo // implement interface IFoo
    {
        private:
            virtual void methodImpl();
    };
    

    Here's an example of why you might do this using a reader that reads from XML and another from DB. Note that common structure is moved into the NVI readFromSource, while non-common behaviour is moved into the private virtual getRawDatum. This way logging and error checking is only needed in the one function.

    class IReader
    {
      public:
        // NVI
        Datum readFromSource()
        {
           Datum datum = getRawDatum();
           if( ! datum.isValid() ) throw ReaderError("Unable to get valid datum");
           logger::log("Datum Read");
           return datum;
        }
      private:
        // Virtual Bits
        Datum getRawDatum()=0;
    };
    
    class DBReader : public IReader
    {
      private:
        Datum getRawDatum() { ... }
    };
    
    class XmlReader : public IReader
    {
       private:
         Datum getRawDatum() { ... }
    };
    
    0 讨论(0)
  • 2020-12-19 15:59

    It may be confusing that once a method is declared as virtual in a base class, it automatically becomes virtual in all derived classes, even if the virtual keywords is not used there. So in your example, both methods of FooBase are virtual.

    ... to deny derived classes the possibility of overriding the common behavior implemented in FooBase::method()...

    If you can get rid of IFoo, and just start the hierarchy with FooBase with a non-virtual method, that would do it. But it looks like you want to allow direct children of IFoo to override method(), but to prevent children of FooBase to override it. I don't think that's possible.

    0 讨论(0)
  • 2020-12-19 16:07

    You could use the pimpl-idiom to achieve this:

    class IFoo
    {
        public:
            IFoo( boost::shared_ptr< IFooImpl > pImpl )
                : m_pImpl( pImpl )
            {}
    
            void method() { m_pImpl->method(); }
            void otherMethod() { m_pImpl->otherMethod(); }
        private:
            boost::shared_ptr< IFooImpl > m_pImpl;
    };
    
    class IFooImpl
    {
        public:
            void method();
            virtual void otherMethod();
    };
    

    Now others can still subclass IFooImpl and pass it to IFoo, but they cannot override the behavior of method (they can override otherMethod). You can even make IFooImpl a direct subclass of IFoo and use enable_shared_from_this to initialize IFoo correctly. This is just the gist of the method. There are many ways to tweak this approach. For instance you can use the factory-pattern to make sure IFoos are created correctly.

    Hope that helps.

    0 讨论(0)
提交回复
热议问题