2016-04-28 15 views
9

的比方说,我们有以下层次:如何防止调用基实现的方法

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()曾经被称为Derived对象,对象将不同步和数据将被破坏的。它继承了Base的数据结构及其操作,但需要执行其他操作,因此只调用Base::foo()将省略这些额外的操作,结果Derived的状态将被破坏。

因此,我想,以防止Base实施foo所以这直接调用:

​​

理想情况下,应该给我的某些种类的编译时错误。或者什么都不做或以其他方式被阻止

但是这可能是我违反规则的多态性和应该使用成分代替但这需要一个额外的大量打字...

+0

在'Base'中,'protected:'而不是'public:'方法有问题吗?此外,这似乎也是一个体面的候选人,可以在'Base'中重新声明为纯虚拟的,*并提供一个'Base'实现,这并不常见,但确实发生了。如果“派生”应该总是被执行,那么它似乎是一个不错的选择。 – WhozCraig

+0

@WhozCraig我不知道我可以提供纯虚拟方法的实现。如果它被那些继承它的实现重新实现,那么这会阻止实现永远被调用吗? – Resurrection

+1

@Resurrection no,这就是为什么'Base :: foo'上的'protected'会进来。但是它*做的*是强制派生类仍然提供覆盖,同时还提供了一个通用的Base实现,它们可以在不抛弃的情况下调用一个额外的成员功能进入不适。 – WhozCraig

回答

22

如何template method pattern

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

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

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

然后

void test(Abstract& obj) { 
    obj.foo(); // the correct foo_impl() will be invoked 
} 
Derived d; 
test(d); // impossible to call the foo_impl() of Base 
+8

一般来说,隐藏多态性隐藏在非多态模板函数后面是一个好主意,不仅在此案件。 – leemes

8

你可以让所有的foo()方法非public,然后有一个非 - virtual函数在Abstract类中简单地称为foo

+1

对于“can”可能会读“可能应该”(作为一般规则 - 即使您不想阻止人们在脚下自己拍摄)。 –

+1

@Martin Bonner - 真实,有效。链接[非虚拟接口](https://en.wikipedia.org/wiki/Non-virtual_interface_pattern)等... –

12

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

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

的图案出现在输入输出流库sync()pubsync()方法。

为防止直接调用并保持一致状态,您需要在堆栈中的正确位置获得foo方法的final实现。如果意图禁止层次结构顶部的直接呼叫,则可以移动_impl方法。

另请参阅非虚拟接口the NVI pattern


请记住,以及该重载方法不必具有相同的访问说明符的Abstract类。您也可以在派生类privateprotected中创建方法;

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

注:除非另有打算,改变了访问说明符可以认为是不好的设计 - 所以基本上如果你更改访问符,应该应该是一个很好的理由这样做。

+0

感谢您的提示!我以前使用过这种模式,但由于某种原因,它没有发生在我身上,它会解决我目前的问题。无论如何,我认为虚拟foo在这种情况下是一种矫枉过正的情况,而具有虚拟foo_impl的Abstract中的非虚拟foo实际上正如其他人所建议的那样。但是,是的,这是为了当我想让真正的完全控制。 – Resurrection

+0

当然,您在模板方法模式中挂钩的级别取决于您想要做什么。自从你在提问中提到这一点以来,我把它放在那里。仅使用纯虚函数维护类也可能是有利的(它可以将链接器依赖关系最小化,例如通过dll边界)。 – Niall

+0

有趣的一点。重写时我会考虑它。关于编辑:这当然是可能的(虽然我在其他地方读过,但是重新标识这样的标识符是一个糟糕的设计),但是在这种情况下,因为Base必须按照Abstract所设想的工作,所以我不能隐藏公共接口或其中的一部分案件。 – Resurrection

相关问题