【问题标题】:Is there a way to block derived classes from further implementing a virtual function?有没有办法阻止派生类进一步实现虚函数?
【发布时间】:2014-08-30 06:40:17
【问题描述】:

似乎在 C++ 中(或者它是通用的 OOP 概念?)once virtual always virtual 的东西成立。我想知道是否有任何方法可以阻止它。我有这种情况,需要我所问的。

假设我有一个像这样的工厂类

class Object;
class GeneralFactory
{
private:
    virtual Object* CreateObject() = 0;
};

现在我有一个专门的工厂,它仍然可以创建对象,但有更多的控制权。

class SpecializedFactory : public GeneralFactory
{
private:
    virtual Object* CreateObject();
    virtual Object* DoCreateObject() = 0;
};

这个想法是让SpecializedFactory::CreateObject 在其实现中使用DoCreateObject。但事实上,一个类可以从SpecializedFactory 派生并覆盖CreateObject,从而破坏了良好的意图。

我正在寻找的东西是可能的、有效的,还是让程序员成为好公民并遵循类层次规则的唯一方法?

什么是合适的替代品?我正在使用 C++03 仅供参考。

【问题讨论】:

    标签: c++ oop virtual-functions c++03


    【解决方案1】:

    似乎在 C++(或者它是一般的 OOP 概念?)中,曾经是虚拟的 总是虚拟的东西。

    我不会称它为“一次虚拟总是虚拟”,因为这听起来有点误导。虚拟性根本不是派生类的业务。虚函数都是关于 base 类的。它是基类,需要知道一个函数是否为虚函数,并在必要时进行虚函数调用。派生类函数可能会说“我不再是虚拟的了!”,但谁在乎呢?那时它已经通过虚函数调用被调用了。

    C++11 final 不会改变这个运行时行为,它只是防止在编译时覆盖。

    我正在寻找的是可能的、有效的,还是唯一的方法是 程序员要成为好公民并遵守类层次规则?

    在 C++03 中,最简单的方法是提供良好的文档并在工作面试中只选择优秀的程序员 :)

    但这可能不是您想要的。这里的技术解决方案是将您的类设计更改为 has-a 关系。 使整个类成为最终类的解决方法确实存在于 C++03 中。

    所以,不是ConcreteSpecializedFactory is-a SpecializedFactory,而是SpecializedFactory has-a SpecializedFactoryImplementation。然后,您可以(可选地,为了更严格)使用friend 来允许后者仅从前者调用,并且(这是有趣的部分出现的地方)您可以使用虚拟继承技巧C++ FAQ "How can I set up my class so it won't be inherited from?" 到整个SpecializedFactory 类最终。

    class SpecializedFactoryImplementation
    {
    public:
        virtual ~SpecializedFactoryImplementation() {}
    
    private:
        SpecializedFactoryImplementation(SpecializedFactoryImplementation const &);
        SpecializedFactoryImplementation &operator=(SpecializedFactoryImplementation const &);
    
        friend class SpecializedFactory;
    
        Object* CreateObject()
        {
            return DoCreateObject();
        }
    
        virtual Object* DoCreateObject() = 0;
    };
    
    class SpecializedFactoryBase
    {
    private:
        friend class SpecializedFactory;
         SpecializedFactoryBase() {}
    };
    
    class SpecializedFactory : public GeneralFactory, private virtual SpecializedFactoryBase
    {
    // ...
    public:
        SpecializedFactory(SpecializedFactoryImplementation* impl) :
            m_impl(impl)
        {
            // null checking omitted for simplicity
        }
    private:
    
        // the GeneralFactory base class should not be copyable
        // anyway, so we do not have to worry about copy constructor
        // or assignment operator
    
        SpecializedFactoryImplementation* const m_impl;
    
        virtual Object* CreateObject()
        {
            return m_impl->CreateObject();
        }
    };
    

    以下内容将无法编译:

    class SpecializedFactoryWrittenByEvilProgrammer : public SpecializedFactory
    {
    public:
        SpecializedFactoryWrittenByEvilProgrammer() : SpecializedFactory(0) {}
    private:
        virtual Object* CreateObject()
        {
            return 0;
        }
    };
    

    以下内容也不会编译:

    // somewhere outside of SpecializedFactory:
    SpecializedFactoryImplementation *s;
    s->CreateObject();
    

    【讨论】:

    • 看起来是个不错的开始。谢谢。
    • 虚拟继承技巧的一个可能缺点是它可能会增加内存中类对象的大小(请参阅here)。
    • 虚拟继承技巧的另一个参考是 Bjarne Stroustrup here。另见他的书“C++ 的设计和演变”第 11.4.3 节。
    【解决方案2】:

    你需要的是final关键字:

    struct base {
       virtual void f();
    };
    struct derived : base {
       void f() final;       // virtual as it overrides base::f
    };
    struct mostderived : derived {
       //void f();           // error: cannot override!
    };
    

    【讨论】:

    • 那么我想这是不可能的,因为这是 C++ 特性。
    • @Dirt:如果final 是不必要的,那么他们就不会将其添加到该语言的下一个版本中......
    • @Mehrdad 我不明白是什么让你评论“如果 final 是不必要的,那么他们就不会将它添加到语言的下一个版本中......”对我来说。我没说没必要!
    • @Dirt:我的意思是如果有办法在 C++03 中实现它,那么他们就没有必要在 C++11 中引入它。
    • @Mehrdad 逻辑不成立; C++11 中有很多东西在 C++03 中是可能的,但更容易表达(例如迭代容器)
    猜你喜欢
    • 2015-11-11
    • 2014-03-22
    • 2018-06-20
    • 1970-01-01
    • 1970-01-01
    • 2018-10-14
    • 2015-10-24
    • 2021-01-12
    • 1970-01-01
    相关资源
    最近更新 更多