Glenn Teitelbaum
Glenn Teitelbaum

Reputation: 10343

Can you prevent inherited private members being called through the parent at compile time?

If you have a feature rich class, possibly one you do not own/control, it is often the case where you want to add some functionality so deriving makes sense.

Occasionally you want to subtract as well, that is disallow some part of the base interface. The common idiom I have seen is to derive and make some member functions private and then not implement them. As follows:

class Base
{
public:
  virtual void foo() {}
  void goo() { this->foo(); }
};

class Derived : public Base
{
private:
   void foo();
};

someplace else:

Base * b= new Derived;

and yet another place:

b->foo();  // Any way to prevent this at compile time?
b->goo();  // or this?

It seems that if the compilation doesn't know that it is derived, the best you can do is not implement and have it fail at runtime.

The issue arises when you have a library, that you can't change, that takes a pointer to base, and you can implement some of the methods, but not all. So part of the library is useful, but you run the risk of core dumping if you don't know at compile time which functions will call what.

To make it more difficult, others may inherit from you class and want to use the library, and they may add some of the functions you didn't.

Is there another way? in C++11? in C++14?

Upvotes: 3

Views: 243

Answers (4)

Yakk - Adam Nevraumont
Yakk - Adam Nevraumont

Reputation: 275878

If you have class A:public B, then you should follow the https://en.wikipedia.org/wiki/Liskov_substitution_principle

The Liskov substitution principle is that a pointer-to-A can be used as a pointer-to-B in all circumstances. Any requirements that B has, A should satisfy.

This is tricky to pull off, and is one of the reasons why many consider OO-style inheritance far less useful than it looks.

Your base exposes a virtual void foo(). The usual contract means that such a foo can be called, and if its preconditions are met, it will return.

If you derive from base, you cannot strengthen the preconditions, nor relax the postconditions.

On the other hand, if base::foo() was documented (and consumers of base supported) the possibility of it throwing an error (say, method_does_not_exist), then you could derive, and have your implementation throw that error. Note that even if the contract says it could do this, in practice if this isn't tested consumers may not work.

Violating the Liskov substitution principle is a great way to have lots of bugs and unmaintainable code. Only do it if you really, really need to.

Upvotes: 1

Nir Friedman
Nir Friedman

Reputation: 17714

Although I don't think your overall situation is good design, and I share many of the sentiments in the comments, I can also appreciate that a lot of code you don't control is involved. I don't believe there is any compile time solution to your problem that has well defined behavior, but what is far preferable to making methods private and not implementing them is to implement the entire interface and simply make any methods you can't cope with throw an exception. This way at least the behavior is defined, and you can even do try/catch if you think you can recover from a library function needing interface you can't provide. Making the best of a bad situation, I think.

Upvotes: 1

kfsone
kfsone

Reputation: 24269

TL;DR: Deriving a class is a contract to provide at least that interface. Subtraction is not possible.

This seems to be what you want to do:

struct Library {
    int balance();
    virtual int giveth(); // overrideable
    int taketh(); // part of the library
};

/* compiled into the library's object code: */
int Library::balance() { return giveth() - taketh(); }

/* Back in header files */
// PSEUDO CODE
struct IHaveABadFeelingAboutThis : public Library {
    int giveth() override; // my implementation of this
    int taketh() = delete; // NO TAKE!
};

So that you can't call taketh() on an IHaveABadFeelingAboutThis even when it is cast as the base class.

int main() {
    IHaveABadFeelingAboutThis x;
    Library* lib = &x;
    lib->taketh(); // Compile error: NO TAKE CANDLE!
    // but how should this be handled?
    lib->balance();
}

If you want to present a different interface than the underlying library you need a facade to present your interface instead of the that of the library.

class Facade {
    struct LibraryImpl : public Library {
        int giveth() override;
    };
    LibraryImpl m_impl;

public:
    int balance() { return m_impl.balance(); }
    virtual int giveth() { return m_impl.giveth(); }
    // don't declare taketh
};

int main() {
    Facade f;
    int g = f.giveth();
    int t = f.taketh(); // compile error: undefined
}

Upvotes: 1

WorldSEnder
WorldSEnder

Reputation: 5044

Let's analyze this, focused on two major points:

class Base
{
public:
    virtual void foo() {} // This 1)
// ... 
class Derived : public Base // and this 2)

In 1) you tell the world that every object of Base offers the method foo() publicly. This implies that when I have Base*b I can call b->foo() - and b->goo().

In 2) you tell the world that your class Derived publicly behaves like a Base. Thus the following is possible:

void call(Base *b) { b->foo(); }
int main() {
    Derived *b = new Derived();
    call(b);
    delete b;
}

Hopefully you see that there is no way call(Base*) can know if b is a derived and thus it can't possibly decide at compile-time if calling foo wouldn't be legal.


There are two ways to handle this:

  • You could change the visibility of foo(). This is probably not what you want because other classes can derive from Base and someone wants to call foo afterall. Keep in mind that virtual methods can be private, so you should probably declare Base as
class Base
{
  virtual void foo() {}
public:
  void goo() { this->foo(); }
};
  • You can change Derived so that it inherits either protected or private from Base. This implies that nobody/only inheriting classes can "see" that Derived is a Base and a call to foo()/goo() is not allowed:
class Derived : private Base
{
private:
    void foo() override;
    // Friends of this class can see the Base aspect
// .... OR
// public: // this way
    // void foo(); // would allow access to foo()
};
// Derived d; d.goo() // <-- illegal
// d.foo() // <-- illegal because `private Base` is invisible

You should generally go with the latter because it doesn't involve changing the interface of the Base class - the "real" utility.


Upvotes: 1

Related Questions