C++ 如何防止方法的基实现调用

C++ 如何防止方法的基实现调用,c++,inheritance,C++,Inheritance,假设我们有以下层次结构: class Abstract { public: virtual void foo() = 0; }; class Base : public Abstract { public: virtual void foo() override; //provides base implementation }; class Derived : public Base { public: virtual void foo() override; //

假设我们有以下层次结构:

class Abstract
{
public:
    virtual void foo() = 0;
};

class Base : public Abstract
{
public:
    virtual void foo() override; //provides base implementation
};

class Derived : public Base
{
public:
    virtual void foo() override; //provides derived implementation
};
如果对
派生的
对象调用了
Base::foo()
,则该对象将取消同步,其数据将损坏。它继承了
Base
的数据结构及其操作,但需要执行额外的操作,因此仅调用
Base::foo()
将忽略这些额外的操作,因此
派生的
的状态将被破坏

因此,我想阻止直接调用
Base
实现
foo
,因此:

Derived d;
d.Base::foo();
理想情况下,应该给我一个某种类型的编译时错误。或者什么也不做或者以其他方式被阻止


然而,我可能违反了多态性规则,应该使用组合,但这需要大量额外的键入…

您可以将所有
foo()
方法设置为非
公共的
,然后在
Abstract
类中有一个非
virtual
函数,它只调用
foo

如何:

然后

您可以探索。它允许更好地控制所涉及方法的执行

class Abstract
{
public:
    virtual void foo() = 0;
};

class Base : public Abstract
{
protected:
    virtual void foo_impl() = 0;
public:
    //provides base implementation and calls foo_impl()
    virtual void foo() final override  { /*...*/ foo_impl(); }
};

class Derived : public Base
{
protected:
    virtual void foo_impl() override; //provides derived implementation
};
在iostreams库中可以通过
sync()
pubsync()
方法看到该模式

为了防止直接调用并保持一致状态,您需要在堆栈中的正确位置获得
foo
方法的
final
实现。如果目的是禁止层次结构顶部的直接调用,则可以向上移动
\u impl
方法

另请参见非虚拟接口


还要记住,重写方法不必具有与
抽象类相同的访问说明符。您也可以将派生类中的方法设置为private或protected

class Abstract
{
public:
    virtual void foo() = 0;
};

class Base : public Abstract
{
    virtual void foo() override; //provides base implementation
};

class Derived : public Base
{
    virtual void foo() override; //provides derived implementation
};

注意:除非另有打算,否则更改访问说明符可能会被视为糟糕的设计-因此基本上,如果您确实更改了访问说明符,那么应该有一个很好的理由这样做。

受保护的
有问题:
而不是
公共:
对于
基本
中的该方法?而fwiw,这似乎也是在
Base
中重新定义为纯虚拟并提供
Base
实现的一个不错的候选者,这并不常见,但确实发生了。如果总是实现
派生的
,这似乎是一个不错的选择。@WhozCraig我不知道我可以为纯虚拟方法提供实现。如果该实现被继承它的人重新实现,这会阻止它被调用吗?@Resurrenction否,这就是为什么
Base::foo
上的
protected
会被调用的原因。但它所做的是强制派生类仍然提供覆盖,同时还提供了一个公共的基类实现,它们可以调用,而无需将额外的成员函数扔进困境中。@WhozCraig哦,我明白了。那是要考虑的事情。嗯,谢谢!另一个要考虑的问题是“这个错误的电话会怎样发生?”一般来说,你想“保护墨菲而不是马基雅维利”。谢谢你的提示!我以前使用过这种模式,但出于某种原因,我没有想到它会解决我当前的问题。无论如何,我认为在这种情况下,虚拟foo是一种过分的手段,抽象的非虚拟foo和虚拟foo_impl就足够了,正如其他人所建议的那样。当然,在模板方法模式中挂接的级别取决于您还想做什么。既然你在问题中提到了那一点,我就把它放在那里了。维护一个只有纯虚函数的类也是有利的(它可以最小化链接器依赖性,例如在dll边界上)。有趣的一点。改写时我会考虑的。关于编辑:这当然是可能的(尽管我在其他地方读到过这样的redaclare标识符是一个糟糕的设计),但在这种情况下不是这样的,因为Base必须按照抽象所设想的那样工作,所以在这种情况下我不能隐藏公共接口或其部分。如果您想要强制访问,这是一个替代方案。除非另有打算,否则更改访问说明符可能是糟糕的设计-因此基本上,如果确实更改了访问说明符,应该有很好的理由这样做。我提到它是一种替代方法,但坦率地说,我更喜欢模板方法模式,并且经常成功地使用它,我喜欢它提供给我的控件。因为“可以”阅读“可能应该”(作为一般规则,即使你不试图阻止人们射中自己的脚)。@Martin Bonner-真的,oblig。一般来说,将多态性隐藏在非多态性模板函数后面是个好主意,不仅仅是在这种情况下。
class Abstract
{
public:
    virtual void foo() = 0;
};

class Base : public Abstract
{
protected:
    virtual void foo_impl() = 0;
public:
    //provides base implementation and calls foo_impl()
    virtual void foo() final override  { /*...*/ foo_impl(); }
};

class Derived : public Base
{
protected:
    virtual void foo_impl() override; //provides derived implementation
};
class Abstract
{
public:
    virtual void foo() = 0;
};

class Base : public Abstract
{
    virtual void foo() override; //provides base implementation
};

class Derived : public Base
{
    virtual void foo() override; //provides derived implementation
};