6

I am writing a C++ header in which I define a

class A {
   // ...
};

that I would like to hide from the outside world (because it may change or even be removed in future versions of this header).

There is also a class B in the same header that has an object of class A as a member:

class B {
public:
   // ...

private:
   A a_;
};

What is a proper way of hiding class A from the outside world?

If I put the definition of A in an unnamed namespace, the compiler issues a warning, so I assume that, due to issues with internal linkage, I should do something else.

Marc Mutz - mmutz
  • 22,883
  • 10
  • 72
  • 86
Bjoern
  • 63
  • 1
  • 3
  • Also see [Uses for anonymous namespaces in header files](https://stackoverflow.com/q/357564/608639), [Hide class type in header](https://stackoverflow.com/q/3525552/608639), [How can I hide a class in C++?](https://stackoverflow.com/q/14092705/608639), [Hiding a C++ class in a header without using the unnamed namespace](https://stackoverflow.com/q/5780918/608639) – jww Sep 28 '17 at 23:22

7 Answers7

10

The right way to go about it in C++ is PIMPL idiom. Alternative solution is to put the class you want to hide into a nested namespace, which is usually called detail. But that will not make it totally private as users will still be exposed to its dependencies, and will be able to use it directly.

6

You could do an inner class:

class B
{
  class A { /* ... */ };
  A a_;
}
Karl von Moor
  • 8,076
  • 3
  • 37
  • 51
  • 1
    Oh yes, that would of course work. Sorry, I forgot to mention that B is a class template (with many parameters) and I implement the copy constructor and assignment operator of A myself, so it would be ugly to have class A (as a nested class) be dependent on all template parameters of B. Do you perhaps have another suggestion? – Bjoern Apr 25 '11 at 17:17
  • @Bjoern I'm not sure if I fully understood you. `template class B` and inside it `class A { A(const A&); A& operator=(const A&); };` wouldn't be a problem, right? I don't understand what you mean with class A is dependent on template parameters of B. – Karl von Moor Apr 25 '11 at 17:24
  • Not at all, but `typename B::A` and `typename B::A` would then be different although `A` does not really depend on any template parameters of B. This would require that I write `class A { template A(const typename B::A&) {}; template A& operator=(const typename B::A&) };` – Bjoern Apr 25 '11 at 17:29
  • @Bjoern Of course, you are right (stupid me). Then I'd recommend [this](http://stackoverflow.com/questions/5780918/hiding-a-c-class-in-a-header-without-using-the-unnamed-namespace/5780976#5780976) answer. – Karl von Moor Apr 25 '11 at 17:35
  • If there were no constructions or assignments of objects of type `typename B::A` to objects of type `typename B::A` (with different template parameters), your solution would have been just fine! And in fact, I could write the constructor and the assignment operator in the general form. So thanks for sharing your thoughts! – Bjoern Apr 25 '11 at 17:40
2

Document that this class is not part of the public API and should not be used.

In C++ you have to trusted programs that link with your library code because you have little other choice. C++ has limited "access control" features many of which can be bypassed or abused so you're better of treating your API clients with respect and building trust.

If you design your API to be easy to use correctly and hard to use unintentionally incorrectly then you will be helping your clients and it is hardly your fault if your clients abuse your interface.

CB Bailey
  • 648,528
  • 94
  • 608
  • 638
2

An unnamed namespace is useless anyways, as it only protects agains multiple definitions. What you could do is either using the pImpl Idiom, as mentioned in other answers, or use a detail namespace. Works fine for Boost:

namespace detail{
  class A{
    // ...
  };
}

class B{
public:
  // ...
private
  A a_;
};

Anyone messing with stuff in a detail namespace is asking for trouble. Or maybe obscure it even more

namespace _b_impl_detail{
  // ...
};

Anyone who now touches anything inside should be shot in the foot. :)

Xeo
  • 123,374
  • 44
  • 277
  • 381
1

Instead of class B holding an A object, have it hold an A* instead (or a shared_ptr<A>, or an unique_ptr<A>, etc.). This way class B only needs a forward declaration of class A and class A can be fully defined inside of class B's source file.

ildjarn
  • 59,718
  • 8
  • 115
  • 201
1

If A is an implementation detail of B, don't put its definition in the header at all. Instead:

class B {

   ...
   class A * myA;
};

and then put the definition of A in the B implementation (i.e. .cpp) file.

  • Sorry, I really forgot to mention that class B is in fact a template (depending on many parameters) and the library should come as a single header only, without a separate implementation .cpp file. – Bjoern Apr 25 '11 at 17:20
0

I'd like to add a small increment over https://stackoverflow.com/a/5780976/1525238 that helped me better solve my peculiar use case, namely where the "main" class is a template and the "helper/inner" class also has to be a template1.

I used a nested namespace called detail, made all "helper" content private and made the "main" class a friend of the "helper" class:

template<__MAIN_TEMPLATE_PARAMS__> class Main;

namespace detail {
    template<__HELPER_TEMPLATE_PARAMS__> class Helper {

        /* All Main templates are friends */
        template<__MAIN_TEMPLATE_PARAMS__> friend class Main; 

        /* Private stuff, not reachable from the outside */
        static void privateThing(){
            ...
        }
    };
}

template<__MAIN_TEMPLATE_PARAMS__> class Main {
    void usePrivateThing(){
        detail::Helper<__DESIRED_HELPER_TEMPLATE_PARAMS__>::privateThing();
    }
};

The private stuff is static above only to make the code shorter. They may very well be tied to the Helper instance.

In retrospect, there could certainly be more elegant solutions involving less black magic, but it highly depends on the specific application. I still find the above a legit, nice use case for a friend class.


1 This is because I needed to use a template helper function that required a partial specialization, which is not allowed yet in c++, for no particular reason but is technically possible with a wrapper class. Partial specialization is omitted from the above for simplicity.

Ayberk Özgür
  • 4,288
  • 4
  • 31
  • 50