0

How can ensure that only "my" code can use a class, even if it is used a base class? (If it's not used as a base class I can make it a private or protected nested class of one of my classes)

If I want to indicate that use of a base class for one of my classes is a mere implementation detail, I can use a private base class:

 class Base
 {
      ...
 }

 class Derived: private Base
 {
 public:

      Derived(...): Base{...} {... };

      ...
 }

To clients of my Derived class, that I used the Base class is not apparent:

 #include "Derived.h"

 void client() {
    Derived d{...};
    Base *b = static_cast< Base * >(&d);// error
    ...
 }

But imagine that the Base class is so specialised, or confusing, or tricky to use, that I don't want it to be possible for clients of my code to use it as a base class or create objects of that class. I want it to be "private", in some sense, to some of my code, so client code like this fails:

 #include "Derived.h"

 class Client: Base// error wanted here
 {
 public:
    Client(...): Base{...} {...};

    ...
 }

 void client()
 {
    Derived d{...};// OK
    Base b{...};// error wanted here
    Client c{...};// error wanted here
 }

How can I do that?

In effect, I am asking how can I achieve something like Java's package-private classes, which are accessible to only other classes in the same "package" (module), but can not be used by code outside the "package".

Raedwald
  • 46,613
  • 43
  • 151
  • 237

3 Answers3

2

You can "enforce" this by convention, by placing the "private" entities into a detail namespaces. Many popular libraries (e.g. Boost) do this:

namespace detail
{
    class Base { /* ... */ };
}

class Derived : private detail::Base
{
    /* ... */
};

When modules will be standardized this problem will be solved properly, as you will be able to control what entities get exported and which ones are implementation details.

Vittorio Romeo
  • 90,666
  • 33
  • 258
  • 416
  • 1
    Definitely this. There's no stopping people from acting foolish. Better just make sure they know it's foolish and leave it up to them to be responsible adults. – StoryTeller - Unslander Monica Nov 30 '17 at 14:21
  • This achieves nothing; a client could just write `detail::Base b = {...};`. – Raedwald Nov 30 '17 at 14:22
  • 3
    @Raedwald: there's no better solution. C++ developers should know that, by convention, all bets are off if you write `detail::`. There's nothing stopping *you* today from using some weird implementation entities from `libstdc++`. – Vittorio Romeo Nov 30 '17 at 14:23
  • 1
    @Raedwald - You are missing the point. Plenty of Machiavellian plots can be employed to break encapsulation. There's no true protection. This protects your clients from using implementation details by accident, which is what one should protect them from at the end of the day. – StoryTeller - Unslander Monica Nov 30 '17 at 14:25
  • Why should a client *know* that the `detail` namespace is off limits, and using it will break encapsulation? By writing a comment in the header file? I could just as easily write a comment saying that the `Base` class is off limits. – Raedwald Nov 30 '17 at 14:29
  • @Raedwald: because `detail` is the most popular name, and it is used in popular libraries. Aim for the [principle of least astonishment](https://en.wikipedia.org/wiki/Principle_of_least_astonishment). | To the downvoter - mind explaining why? – Vittorio Romeo Nov 30 '17 at 15:08
  • https://github.com/search?utf8=%E2%9C%93&q=org%3Aboostorg+namespace+detail&type=Code – Vittorio Romeo Nov 30 '17 at 15:09
1

This can't be done directly as you would do in Java. If it's only a matter of avoiding confusion you can move Base inside a namespace which is meant to be ignored by clients of your code, eg:

namespace hidden {
  class Base {
    ..
  };
}

class Derived : private hidden::Base {
  ...
};

If instead you really want to avoid the possibility of using Base then it's quite a difficult story if you plan to use Base as a parent of multiple classes (which amount could vary over time). You could give Base a private constructor, and indicate that each of your derived classes is a friend of Base:

class Hider {
  private:
    Hider() = delete;
    class Base {
      ..
    };

  friend class Derived;
};

class Derived : Hider::Base {
  ..
};

Of course this requires manual maintenance for each new class you want to derive from Base.

Jack
  • 131,802
  • 30
  • 241
  • 343
  • Any reason you can't make the base class a private member of another class; and then friend things that are to use it? – UKMonkey Nov 30 '17 at 14:22
  • @UKMonkey how to you hide that class then? –  Nov 30 '17 at 14:22
  • @Ivan why do you care if that 'container' class is hidden or not? You can make it so that there's a compile error if someone tries to create an instance of it – UKMonkey Nov 30 '17 at 14:24
1

If you want to enforce it 100%, and don't like the python method of" please don't use things that start with '_'" then I believe this is your port of call:

class Dave;

class MyPrivateBaseClasses {
  private:
    MyPrivateBaseClasses();    // ensure nothing can use this class
    class BaseClassA {};

    friend Dave;
};

class Dave : public/private MyPrivateBaseClasses::BaseClassA
{};

Sure - it means you have to friend everything that wants to use it, but it does give you exactly what you wanted; 100% protection against people using BaseClassA.

UKMonkey
  • 6,941
  • 3
  • 21
  • 30
  • Except it still doesn't protect against maliciousness (very little can do that), such as the end-user-programmer adding their type to the list. Honestly the only real protection I can think of is to not distribute the source of the hidden base class, but that may well not be feasible, and still may not be enough depending on the level of maliciousness you need to protect against. Of course, I would say if you need to protect against that degree of maliciousness what are you doing distributing source for someone else to compile? – SoronelHaetir Nov 30 '17 at 16:02
  • Well, it doesn't prevent people editing the code - of course - but it prevents people using it at all WITHOUT editing the code. The use of namespaces as per the other answers requires no edit of the code to use the classes. – UKMonkey Nov 30 '17 at 16:31