让我们说我们有以下层次结构:
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
};
Run Code Online (Sandbox Code Playgroud)
如果Base::foo()在Derived对象上调用该对象将使其同步并且其数据将被破坏.它继承了Base数据结构及其操作,但需要执行其他操作,因此只调用Base::foo()将省略这些额外操作,因此Derived状态将被破坏.
因此我想阻止直接Base执行foo这样的:
Derived d;
d.Base::foo();
Run Code Online (Sandbox Code Playgroud)
理想情况下,应该给我一些编译时错误.或者什么都不做或以其他方式阻止.
但是,我可能违反了多态性规则,应该使用组合,但这需要大量的额外输入......
son*_*yao 22
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
};
Run Code Online (Sandbox Code Playgroud)
然后
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
Run Code Online (Sandbox Code Playgroud)
Nia*_*all 12
您可以探索模板方法模式.它允许更好地控制所涉及方法的执行.
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
};
Run Code Online (Sandbox Code Playgroud)
使用sync()和pubsync()方法可以在iostreams库中看到该模式.
要防止直接调用并保持一致状态,您需要在堆栈中的正确位置获取方法的final实现foo.如果目的是禁止从层次结构顶部直接调用,则可以向上移动_impl方法.
另请参见非虚拟接口,即NVI模式.
还要记住,重写方法不必具有与Abstract类相同的访问说明符.您也可以只让在派生类中的方法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
};
Run Code Online (Sandbox Code Playgroud)
注意:除非另有说明,否则更改访问说明符可能被认为是错误的设计 - 因此,如果您确实更改了访问说明符,那么应该有充分的理由这样做.