4

我在 C++ 中有以下类层次结构:

class Base {
    virtual void apply() = 0;
};

class Derived : public Base {
    virtual void apply() {
        // implementation here that uses derived_specialty
    }

    virtual void derived_specialty() = 0;
};


class Implementation : public Derived {   
    virtual void derived_specialty() {
        // implementation
    }
};

我想保证实现级别的类不提供它们自己的应用实现,并且它们只实现派生专业。有没有办法保证继承自 Derived 的类不会实现 apply,从而使用 Derived::apply 实现?我的理解是,在 C++ 中,基类中的虚拟方法在继承层次结构中一直是虚拟的,但是如果 C++ 中有任何技巧可以完成,我很想听听它们。

我总是对 C++ 允许的事情感到惊讶,所以我认为值得一问。:)

4

8 回答 8

21

您可以使 Implementation 成为委托类,而不是 Derived 的专业化

class Derived : public Base
{
    Derived()

    void apply() 
    {
        //whatever, delegate to impl class instance
        impl->apply_specialization();
    }


    Impl* impl;
};

class Impl : public WhateverImplInterface
{
      void apply_specialization(){}
};

然后实现无权访问 apply 函数并与层次结构分离。Derived 类然后由 Impl 类的实例参数化。

于 2009-02-14T21:48:11.003 回答
5

你可以通过组合来做到这一点:

class Base {
    virtual void apply();
};

class Derived : public Base {

    class IImplementation {
        virtual void derived_specialty() = 0;
    };

    IImplementation& m_implementation;

    Derived(IImplementation& implementation)
        : m_implementation(implementation)
    {}

    virtual void apply() {
        // implementation here that uses m_implementation.derived_specialty
    }

};


class Implementation : Derived::IImplementation {   
    virtual void derived_specialty() {
        // implementation
    }
};

其他类仍然可以继承 Derived 并覆盖 apply 方法,但您的 Implementation 类不再是这些类之一。

于 2009-02-14T21:41:56.353 回答
5

在您的文档中明确限制。

于 2009-02-14T21:46:27.840 回答
5

“我想保证实现级别的类不提供自己的应用实现。”

你不能。

到目前为止,我看到的所有示例都没有阻止任何派生类定义它们自己的 apply 函数。它们都提供了对 apply 和 derived_specialty 之间的关系进行建模的方法,建议用户不要覆盖 apply。不过,您可以在一行文档中实现相同的目的。

您正在寻找的是 C++ 中不存在的 Java final 语句,对吗?

于 2009-02-14T23:43:53.517 回答
4

您可以使 Base::apply 非虚拟并在 Base 中使用模板方法模式。

本文解释了这种做法的优点:
http ://www.gotw.ca/publications/mill18.htm

于 2009-02-14T21:49:53.307 回答
4

您可以在析构函数中放置一个断言,以确保apply未被覆盖:

class Base {
    virtual void apply() = 0;
};

class Derived : public Base {
    virtual void apply() {
        // implementation here that uses derived_specialty
    }
    virtual ~Derived() {
        assert(this->apply == Derived::apply);
    }
    virtual void derived_specialty() = 0;
};


class Implementation : public Derived {   
    virtual void derived_specialty() {
        // implementation
    }
};

这里的想法是 this->apply 将从虚拟表中获取方法地址,而 Derived::apply 在编译时解析它。如果它们相等,则 apply 在 Implementation 类中不会再次被覆盖。这种方法还有一个优点是它不会在发布版本中施加性能损失,其中 assert() 宏被(应该)从生成的代码中剥离。

于 2009-02-15T00:01:28.217 回答
1

尝试使用模板方法模式

维基百科有一个 C++ 示例。

它不会改变封装,但它改进了设计,所以你不需要。

于 2009-02-15T00:32:20.173 回答
1

总是有访问修饰符:

 class base {
      protected: virtual void real_apply() = 0;
 };
 class derived : public base {
      void real_apply();
 public:
      apply() { real_apply(); }
 };
 class other : public derived {
      void func() {
          apply();      // this is ok
          real_apply(); // this is a compile time error
      }
 };
于 2009-05-05T19:02:27.413 回答