代码之家  ›  专栏  ›  技术社区  ›  andreas buykx

如何使用C++中的非虚接口成语实现接口类?

  •  3
  • andreas buykx  · 技术社区  · 14 年前

    在C++中,接口可以由纯纯的类实现。

    class Lib::IFoo
    {
        public:
            virtual void method() = 0;
    };
    

    :

    class Lib::Bar
    {
        public:
            void stuff( Lib::IFoo & );
    };
    

    Lib::Bar ,所以我必须执行 IFoo 接口。

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

    非虚拟接口(NVI)习惯用法应该拒绝派生类重写在 FooBase::method() ,但是自从 伊福 FooBase::方法() .

    如果我想使用NVI习惯用法,除了已经建议的pImpl习惯用法(感谢space-c0wb0y)之外,还有什么其他选择。

    4 回复  |  直到 5 年前
        1
  •  5
  •   Michael Anderson    14 年前

    我想你的NVI模式是错的: http://en.wikibooks.org/wiki/More_C%2B%2B_Idioms/Non-Virtual_Interface

    但不确定这是否能解决你的问题。

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

    下面是一个示例,说明为什么可以使用从XML读取数据的读取器和从DB读取数据的读取器来执行此操作。注意,公共结构被移到NVI readFromSource中,而非公共行为被移到私有虚拟getrawdatam中。这样,只需要在一个函数中记录日志和检查错误。

    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() { ... }
    };
    
        2
  •  4
  •   sbi    14 年前

    一部分

    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
    };
    

    派生类可以插入 f() 在他们注定要改变的地方 属于 f()

        3
  •  2
  •   DS.    14 年前

    当一个方法在基类中声明为virtual时,即使 virtual 此处不使用关键字。在你的例子中,两种方法 FooBase 是虚拟的。

    超越共同点的可能性 在中实现的行为 FooBase::method()。。。

    如果你能摆脱 IFoo 食物库 使用非虚拟 method ,那就行了。但看起来你想允许 重写 method() ,但为了防止 来覆盖它。我不认为那是可能的。

        4
  •  1
  •   Björn Pollex    14 年前

    您可以使用pimpl习惯用法来实现这一点:

    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();
    };
    

    现在其他人仍然可以子类 IFooImpl 把它传给 IFoo method (它们可以覆盖 otherMethod ). 你甚至可以 IFooImpl公司 伊福 和使用 enable_shared_from_this 伊福 正确地。这就是方法的要点。有很多方法可以调整这种方法。例如,您可以使用 factory-pattern 伊福 正确创建了。