Hiding interfaces from the user. C++

Started by
10 comments, last by Mufflot 13 years, 4 months ago
In my current project I don't want a user to be able to create an object, which is handled internally by some other class. F.ex. if I have this Time class, which is created when the engine is created, I want to hide the constructor. My current code is like this:

class Time{public:    float GetDeltaTime();    ...private:    Time();     ...   friend class Engine;}


By going with this method, I declare friendships everywhere. I'm wondering, can this be done in some way without friendships?

Mufflot
Advertisement
You can hide/prevent the object creation/deleting from user for example by creating interface for your Time class like this:

// Time interface.class Time{protected:    // protected destructor prevents the user from deleting your time object.    virtual ~Time()    {    }public:    virtual float GetDeltaTime() = 0;};// Engine interface.class Engine{public:    virtual ~Engine()    {    }    virtual Time* GetTime() = 0;};


In somewhere you have implemented the Time and Engine interfaces like this:

// Class which implements the time interface.class TimeImplementation: public Time{public:    TimeImplementation()    {    }    virtual ~TimeImplementation()    {    }    virtual float GetDeltaTime()    {        // TODO: Implement!        return 1.0f;    }};// Class which implements the engine interface.class EngineImplementation: public Engine{public:    EngineImplementation()    {    }    virtual ~EngineImplementation()    {    }    virtual Time* GetTime()    {        return &iTime;    }private:    TimeImplementation iTime;};// Function which creates the engine.Engine* CreateEngine(){    return new EngineImplementation;}


Here is an example code for creating and using the engine.

void example(){    Engine* engine = CreateEngine();    Time* time = engine->GetTime();    float deltaTime = time->GetDeltaTime();    delete engine;}


There is the pimpl-idiom, where the implementation is only visible to internal places, and the interface would have a pointer to that forward declared implementation. This could help you emulate .net's internal visibility.

[nitpick: your class declaration is missing a semicolon]

edit: s/impl/pimpl/
I would just put the headers in a directory called "internal" or "private" or something like that. It communicates clearly that the classes should not be used outside the API and it doesn't clutter the code.
Thanks for the replies!

I'm going to try out ttstreamer's solution, it looked quite nice.
Quote:Original post by Mufflot
Thanks for the replies!

I'm going to try out ttstreamer's solution, it looked quite nice.


So how do you prevent users from creating an instance of TimeImplementation?
Generally you do that by just not putting the implementation classes in headers at all.
jonathanjansson: Haha yeah I actually missed the part where XImplementation's constructor was public ;).

SiCrane: Sounds like a good idea, any tips on how can you do that? I'm not at my home so I can't really try it out by myself atm.
Quote:Original post by Mufflot
SiCrane: Sounds like a good idea, any tips on how can you do that? I'm not at my home so I can't really try it out by myself atm.


The post right in the middle between your "I'm wondering" and "Thanks for the replies!" contains a clue, as does this post.

pimpl
The first question you should ask yourself when trying to hide something from the user isn't how, but why. Is there any good reason your user shouldn't be able to create their own Time instances? And by "good reason" I mean will it break your engine in any way if a second one is created, or are you just doing this out of a sense of "you should only need one of these."

If it won't break your engine to create a second Time instance*, then don't bother hiding it from the user. They can access your engine's instance if they want to, or maybe they'll think of something you haven't and will want to make a second one for some reason. Generally speaking, the only good reason to hide something from the user is if it has the potential to break things and you need to limit access in a way that avoids that potential**.

-----
* In such a circumstance you should be using a singleton design anyway, or even better, changing the design so that this won't be an issue.

** Good example: for classes that expect to be managed by a shared_ptr or intrusive_ptr or such, you might decide to use a private constructor with the named constructor idiom which creates them inside the appropriate smart pointer so the user can't forget to put them in one.

This topic is closed to new replies.

Advertisement