在C ++中防止虚拟方法实现

时间:2009-02-14 21:32:00

标签: c++ inheritance encapsulation

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

我想保证实现级别的类不提供自己的apply实现,并且它们只实现derived_specialty。有没有办法保证继承自Derived的类不会实现apply,以便使用Derived :: apply实现?我的理解是在C ++中,在Base类中使虚拟的方法在继承层次结构中一直是虚拟的,但是如果C ++中有任何技巧要完成,我会有兴趣听到它们。

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

8 个答案:

答案 0 :(得分:21)

您可以使实现成为委托类而不是派生

的特化
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类的实例参数化。

答案 1 :(得分: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类不再是这些类中的一个。

答案 2 :(得分:5)

在文档中明确限制。

答案 3 :(得分:5)

“我想保证实施层面的课程不提供自己的申请实施。”

你不能。

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

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

答案 4 :(得分:4)

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

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

答案 5 :(得分: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将在编译时解析它。如果它们相等,则在Implementation类中不会再次覆盖apply。这种方法的优点还在于它在发布版本中没有性能损失,其中assert()宏(应该)从生成的代码中剥离。

答案 6 :(得分:1)

尝试使用模板方法模式

Wikipedia有一个C ++示例。

它不会改变封装,但它改进了设计,因此您不需要。

答案 7 :(得分: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
      }
 };