11

Is it somehow possible, to accomplish the following:

x.hpp - this file is included by many other classes

class x_impl; //forward declare
class x {
    public:
        //methods...
    private:
        x_impl* impl_;
};

x.cpp - the implementation

#include <conrete_x>
typedef concrete_x x_impl;    //obviously this doesn't work
//implementation of methods...

So basically, I want the users to include the file x.hpp, but be unaware of the conrete_x.hpp header.

Since I can use concrete_x only by a pointer and it appears only as a private data member, a forward declaration should be enough for the compiler to know how much space to prepare for it. It looks quite like the well-known "pimpl idiom".

Can you help me with this?

PS. I don't want to use a void* and cast it around..

6
  • why can't you have concrete_x inherit from x_impl? Commented Nov 6, 2012 at 17:54
  • What exactly is your problem? Commented Nov 6, 2012 at 17:54
  • Why don't you want to define class x_impl itself in <concrete_x>? Commented Nov 6, 2012 at 17:55
  • concrete_x is a library class that I am trying to hide Commented Nov 6, 2012 at 17:57
  • 3
    Mandatory reading Commented Nov 6, 2012 at 17:57

4 Answers 4

9

Actually, it's even possible to completely hide from the user:

// Foo.hpp
class Foo {
public:

    //...

private:
    struct Impl;
    Impl* _impl;
};

// Foo.cpp
struct Foo::Impl {
    // stuff
};

I would just like to remind you that:

  • you will need to write a proper destructor
  • and thus you will also need a proper copy constructor, copy assignment operator, move constructor and move assignment operator

There are ways to automate PIMPL, at the cost of some black magic (similar to what std::shared_ptr does).

Sign up to request clarification or add additional context in comments.

6 Comments

Please take a look at @Bart van Ingen Schenau's idea. What is your solution better at?
@elmes: The name Impl is completely hidden, whereas Bart's solution introduces a x_impl name in the enclosing namespace. It's just even more hidden with a private nested structure.
Would this work if the struct is inside a namespace in cpp file?
@ChaoSXDemon: Any kind of forward declaration works, because you do not need a complete type to form a pointer. Declaring the struct as private is merely reinforcing that nobody else should care about it.
@MatthieuM. Why using struct instead of class I've search about its use for hiding implementation and can't find out why struct is used... Thanks!
|
5

As an alternative to the answer from @Angew, if the name concrete_x should not be made known to users of class x, you could do this:

in x.hpp

class x_impl;
class x {
  public:
    x();
    ~x();
    //methods...
  private:
    x_impl* impl_;
};

in x.cpp

#include <concrete_x>
class x_impl : public concrete_x { };

x:x() : impl_(new x_impl) {}
x:~x() { delete impl_; }

4 Comments

Yes, this is THE solution. It's not universal in C++11 though, since concrete_x could be final. One final question: what is the performance / memory cost of deriving the class just to hide it?
Compared to directly storing a pointer to concrete_x, there is no performance / memory cost. Just a (small) maintenance cost in that the maintainer needs to understand it.
Could you explain what's going on with concrete_x? The exemplary header is missing, and its not clear to me why the include is present in x.cpp versus adding the class definition of concrete_x to the top of x.cpp.
@jww: In the question, <concrete_x> is a existing header that declares the class concrete_x, which must not be included/known outside of x.cpp. That is why I am including the header, rather than declaring the concrete_x class directly in the x.cpp file.
2

This will only work when the forward declaration declares the actual name of the class. So either change x.hpp to:

class concrete_x;
class x {
    public:
        //methods...
    private:
        concrete_x* impl_;
};

or use the name x_impl for the class defined in the header <concrete_x>.

Comments

0

That's what interfaces are for. Define an interface (pure virtual class) in your shared header file and give it to users. Inherit your concrete class from the interface and put it in the non-shared header file. Implement the concrete class in the cpp file (you can even define the concrete class inside the cpp).

2 Comments

This seems okay, only that I'll have a virtual class only to hide the detail of a private member; C++ absurd.
@elmes, if your'e thinking about the possibility of different concrete_x's than it's the most natural approach...

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.