Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to prevent call to base implementation of a method

Lets say we have following hierarchy:

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

If Base::foo() is ever called on the Derived object that object will desync and its data will be corrupted. It inherits Base's data structure and its manipulation but needs to perform additional operations so calling only the Base::foo() will omit these extra operations and as a result the Derived's state will be corrupted.

Therefore I would like to prevent direct call of Base implementation of foo so this:

Derived d;
d.Base::foo();

ideally, should give me a compile time error of some sorts. Or do nothing or otherwise be prevented.

However it might be I am violating the polymorphism rules and should use composition instead but that would require a lots of extra typing...

like image 646
Resurrection Avatar asked Apr 28 '16 08:04

Resurrection


3 Answers

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

then

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
like image 196
songyuanyao Avatar answered Oct 28 '22 12:10

songyuanyao


You can explore the template method pattern. It allows for greater control of the execution of the methods involved.

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

The pattern is seen in the iostreams library with sync() and pubsync() methods.

To prevent the direct calls and maintain the consistent state, you will need to get the final implementation of the foo method in the correct place in the stack. If the intent is to prohibit the direct call from the top of the hierarchy, then you can move the _impl methods up.

See also the non-virtual interface, the NVI pattern.


Bear in mind as well that the overriding methods do not have to have the same access specifier as the Abstract class. You could also just make the methods in the derived classes private or 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
};

Note: unless otherwise intended, changing the access specifier could be considered bad design - so basically if you do change the access specifier, there should should be a good reason to do so.

like image 45
Niall Avatar answered Oct 28 '22 14:10

Niall


You can make all the foo() methods non-public, then have a non-virtual function in the Abstract class that simply calls foo.

like image 29
Tony Delroy Avatar answered Oct 28 '22 14:10

Tony Delroy