views:

69

answers:

3

Say I have an abstract class

class NecessaryDanger
{
public:
     virtual void doSomethingDangerous() =0;
}

and a class that is derived from this class:

class DoesOtherStuff : public NecessaryDanger
{
     //stuff
     void otherMethod();
     void doSomethingDangerous();
}

is there a way I can only allow access of doSomethingDangerous() like

DoesOtherStuff d;
d = DoesOtherStuff();
d.otherMethod(); //OK
d.doSomethingDangerous(); //error

NecessaryDanger* n = &d;
n->doSomethingDangerous(); //OK

I am not very good at C++ yet, so the code above may not be quite right, but you maybe get the idea. I have a set of classes that need to have the ability to do "something dangerous" (in their own special way) that could cause problems if more than one object of these classes does this dangerous thing. I would like to have a manager class that has a NecessaryDanger pointer to only one object. If the method doSomethingDangerous could only be called by a NecessaryDanger object, then it would be more difficult for an accidental call to doSomethingDangerous to happen and cause me headaches down the road.

Thanks in advance for the help. Sorry in advance if this is a dumb question!

+4  A: 

Sure. Just make it private in the derived class and public in the base.

Of course, if NecessaryDanger is a public base, then anyone can cast and call. You might want to make it a private base and use friend.

class DoesOtherStuff : private NecessaryDanger
{
     //stuff
     void otherMethod();

private:
     void doSomethingDangerous();

     friend class DangerManager;
}
Potatoswatter
Could you please explain how I would go about doing this with friend? Would I make derive from the base like class derived : private base, make the dangerous method private and a declare the base class as friend?Why does a making the base private prevent casting?Thanks
Johnny
Updated with code sample. It's pretty much as you describe, but make the manager class the friend: you want it to have full access to `private` things. Making the base private prevents casting to the base because, when something is private, nothing outside the class itself and its friends can tell it exists.
Potatoswatter
Thanks for the edit! But won't this allow me to call this method anywhere within DangerManager for any object? And what if I need to call doSomethingDangerous from somewhere outside of DangerManager later in some other context?I would still call this from within danger manager like DoesOtherStuff d = DoesOtherStuff(); d.doSomethingDangerous; right? Is there a way I would only be able to call this as NecessaryDanger* n; n->doSomethingDangerous()? Thanks for bearing with me.
Johnny
@Johnny: DangerManager exists to call this method, so it should have access, right? The DangerManager would be able to call it either way, using the direct object or a base pointer. Don't worry about it having a little too much access… The C++ permission system is designed to strongly discourage users from accessing an implementation, it is not a security measure or a super-precise tool within the implementation.
Potatoswatter
I think I will just make it private in the derived and public in the base. Otherwise I won't be able to call it as a NecessaryDanger, which is really what I was going for. I just want calls to this function to be obvious and hard to do accidentally (like when looping through all the objects this manager will manage.)Thanks very much for your help!
Johnny
+2  A: 

Remove the virtual classifier in the superclass so that the compiler does compile-time binding based on the variable type instead of run-time binding based on the object type.

Ignacio Vazquez-Abrams
How does eliminating `virtual` solve the problem? That would be more likely to break everything.
Potatoswatter
Calling `doSomethingDangerous()` on a `NecessaryDanger` variable will call `NecessaryDanger::doSomethingDangerous`, regardless of what type the object contained in the variable is. Removing `virtual` breaks polymorphism for that method, which is what the asker wants.
Ignacio Vazquez-Abrams
How did he indicate he doesn't want polymorphism?
Potatoswatter
More to the point, why recommend removing `virtual` from the base but not removing the implementation from the derived class, when he very specifically wants to access via the base pointer.
Potatoswatter
A: 

Building on Potatswatter's response :)

Here is Herb's advice: (especially 1 and 2) applicable in this context.

Guideline #1: Prefer to make interfaces nonvirtual, using Template Method.
Guideline #2: Prefer to make virtual functions private.
Guideline #3: Only if derived classes need to invoke the base implementation of a virtual function, make the virtual function protected.

For the special case of the destructor only:

Guideline #4: A base class destructor should be either public and virtual, or protected and nonvirtual.

Chubsdad