如何在 C++ 中使用非虚拟接口惯用法实现接口类?

发布于 2024-08-31 02:07:08 字数 806 浏览 2 评论 0原文

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

这样的类可以是库的一部分,用于描述对象应该实现哪些方法才能与库中的其他类一起工作::

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

现在

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

我想使用类Lib::Bar,所以我必须实现 IFoo 接口。

出于我的目的,我需要一整套相关的类,因此我想使用一个基类来保证使用 NVI 惯用法的共同行为:

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

    private:
        virtual void methodImpl();
};

非虚拟接口 (NVI) 惯用法应该拒绝派生类重写共同行为的可能性在 FooBase::method() 中实现,但由于 IFoo 将其虚拟化,似乎所有派生类都有机会重写 FooBase::method()

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

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 implement to be able to work with other classes in the library:

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

:

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

Now I want to to use class Lib::Bar, so I have to implement the IFoo interface.

For my purposes I need a whole of related classes so I would like to work with a base class that guarantees common behavior using the NVI idiom:

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

    private:
        virtual void methodImpl();
};

The non-virtual interface (NVI) idiom ought to deny derived classes the possibility of overriding the common behavior implemented in FooBase::method(), but since IFoo made it virtual it seems that all derived classes have the opportunity to override the FooBase::method().

If I want to use the NVI idiom, what are my options other than the pImpl idiom already suggested (thanks space-c0wb0y).

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(4

等你爱我 2024-09-07 02:07:08

我认为你的 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,而非常见行为则移至私有虚拟 getRawDatum。这样,仅在一个函数中需要记录和错误检查。

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

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() { ... }
};
灼疼热情 2024-09-07 02:07:08

通常,使用 NVI(有时也称为“模板方法”)的原因是派生类应该只更改基类行为的一部分。因此,您要做的就是:

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()方面 > 的行为,而不会扰乱其基本算法。

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.

晚风撩人 2024-09-07 02:07:08

可能会令人困惑的是,一旦一个方法在基类中声明为虚拟方法,它就会在所有派生类中自动变为虚拟方法,即使那里没有使用 virtual 关键字。因此,在您的示例中, FooBase 的两个方法都是虚拟的。

...拒绝派生类
超越共同点的可能性
实施的行为
FooBase::method()...

如果您可以摆脱 IFoo,并使用非虚拟 methodFooBase 启动层次结构>,这样就可以了。但看起来您希望允许 IFoo 的直接子级覆盖 method(),但阻止 FooBase 的子级覆盖它。我认为这是不可能的。

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.

素罗衫 2024-09-07 02:07:08

您可以使用 pimpl-idiom 来实现此目的:

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 的行为code> (它们可以覆盖 otherMethod)。您甚至可以使 IFooImpl 成为 IFoo 的直接子类并使用 enable_shared_from_this 正确初始化 IFoo。这只是该方法的要点。有很多方法可以调整这种方法。例如,您可以使用 工厂模式 来确保 IFoo s 已正确创建。

希望有帮助。

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.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文