views:

299

answers:

6

Suppose I want to have an inheritance hierarchy like this.

class Base

class DerivedOne : public Base

class DerivedTwo : public Base

The base class is not meant to be instantiated, and thus has some pure virtual functions that the derived classes must define, making it an abstract base class.

However, there are some functions that you would like your derived classes to get from your base class. These functions modify private data members that both DerivedOne and DerivedTwo will have.

class Base {
public:
      virtual void MustDefine() =0; // Function that derived classes must define
      void UseThis(); // Function that derived classes are meant to use
};

However, the UseThis() function is meant to modify private data members. That's where the question comes in. Should I give the Base class dummy private data members? Should I give it protected data members (and thus the derived classes won't declare their own private data members). I know the second approach will decrease encapsulation.

What is the best approach to a situation like this? If a more detailed explanation is needed I'd be happy to provide it.

+5  A: 

If those member variables are supposed to exist in all derived classes then you should declare them in the base class. If you are worried about encapsulation, you can make them private and provide protected accessor methods for derived classes.

Mehrdad Afshari
+1 for protected accessors
AlexKR
Thanks. This is a great option that I didn't even consider.
Anonymous
However, then wouldn't the UseThis() function have to be virtual because I would need to modify it to use the accessors? Unless I implemented the accessors in the base class version as well?
Anonymous
If `UseThis` is the only way derived classes are allowed to modify private variables, you can choose not to provide set accessors. Derived classes would use `UseThis` directly to modify the variables. If they ever need to read the values of those variables, you can provide protected get accessors. I can't see why you might need to make `UseThis` virtual (assuming its implementation is similar for all derived classes).
Mehrdad Afshari
What I'm saying is that the base class's UseThis implementation would be accessing the members directly. The derived class's wouldn't be able to use the implementation because they can't access the member's directly.
Anonymous
Anon: If derived classes should be able to alter the implementation of `UseThis`, you have no option but to make them access the variable; either directly (by making the variables themselves `protected`) at the cost of encapsulation or through a set of `protected` accessors to `private` variables that you can control in the base class. With accessors, you can always limit derived classes to a well defined set of ways to modify the variables.
Mehrdad Afshari
Oh, by the way, I might have misunderstood your last comment. Certainly, derived classes **do not need** to access members directly in order to call `UseThis`.
Mehrdad Afshari
Base::UseThis() is public so of course any derived classes (or in fact all code) can use this implementation. If the derived classes need to access those member variables, it can because as @Mehrdad suggests their accessors are protected (which means just that, accessible for derived classes); or make the member variables themself protected.
digitalarbeiter
Okay. Just to make it clear, I don't want to change the implementation of UseThis(), but if I implemented protected accessors I would have to, or have the base class use it's own accessors in it's implementation because the derived classes have to use accessors to get access to those variables, and they are inheriting the implementation of UseThis(). I don't really get what you're saying regarding your seen. If UseThis() modifies the base class' private members directly they certainly can't use it.
Anonymous
Thus making the members protected seems the only way to go.
Anonymous
Or using dummy members? Which no one has even mentioned.
Anonymous
@Anon: No. As long as you can call `UseThis`, you can change private members **through `UseThis`** regardless of your direct access to those variables. This is what encapsulation is all about; e.g. you can't change your bank balance directly (similar to setting the variable here) but you can do it indirectly by depositing money in your account (similar to calling `UseThis`).
Mehrdad Afshari
Thanks, this is exactly what I needed to know.
Anonymous
A: 

You will have to define Base::UseThis(), in the body of which you will make use of Base's fields (which you will also need to declare in the class definition above). If you only need to access them in UseThis, they can be private. If DerivedOne/Two will need access to them, you should make them protected.

Grumdrig
+1  A: 

Encapsulation is sometimes overrated. If your base class and derived classes need to access those members, then they should probably be protected, not private. If it really is something that needs to be encapsulated, then you may want to make them private but provide getters and setters (either make them private to Base, with getters and setters defined there, or private to the derived classes, with pure virtual getters and setters in Base).

It's a bit hard to give you more specific advice without knowing about the actual problem you're trying to solve.

Brian Campbell
+2  A: 

Another five cents: the good practice is to have abstract interface class which has no other members, but only public pure virtual methods and often public virtual destructor. Then you create base implementation which can also be abstract but can have protected fields, etc.

In you case it would be something like:

class IBlaBla;

class BlaBlaBase : public IBlaBla;

class DerivedOne : public BlaBlaBase

class DerivedTwo : public BlaBlaBase

This allows you to have more flexibility in the future if you decide that Base is no longer good for some specialized task.

Should I give the Base class dummy private data members?

If you can implement a part of functionality without exposing the details to the derived classes, then do it in base class. If your derived classes would need access to these members, provide setters and getters. However, it is not convenient to have setters available for derived classes because your code becomes tightly coupled.

AlexKR
That is the recommended practice, but I have seen several cases in which it really does make sense to have data members in the abstract base class. I do think that any design that results in private data members in the base class should be thought about carefully though.
Omnifarious
@Omnifarious, yeah, I just updated the post. The idea is that it is fine to have private members in base class, but it is not good to change these fields from derived. If you are sure that derived classes would not need to change the private things, then it is fine to have them in base class.
AlexKR
A: 

Here is a possible resolution to your dilemna:

class Base {
 public:
   virtual ~Base() {}

   virtual void redefine_me() = 0;
   void utility_function();

 private:
   virtual int get_data_member() = 0;
   virtual void set_data_member(int v) = 0;
};

class Derived1 : public Base {
 public:
   virtual void redefine_me() { do_d1_stuff(); }

 private:
   int my_private_idaho_;
   virtual int get_data_member() { return my_private_idaho_; }
   virtual void set_data_member(int v) { my_rpviate_idaho_ = v; }
};

class Derived2 : public Base {
 public:
   virtual void redefine_me() { do_d2_stuff(); }

 private:
   int gomer_pyle_;
   virtual int get_data_member() { return gomer_pyle_; }
   virtual void set_data_member(int v) { gomer_pyle_ = v; }
};

void Base::utility_function()
{
   set_data_member(get_data_member() + 1);
}

It's biggest disadvantage is that now access to the private data member is mediated by a virtual function call, which isn't the cheapest thing around. It's also hidden from the optimizer.

This means that if you choose it, you should adopt a pattern where you fetch the private data member into a local variable at the beginning of your utility function and set it from the local variable before you return. Of course some utility functions may call out to functions that require the object state to be updated before they're called, and this pattern would then have to be modified to account for that. But then again, such utility functions are likely not to be able to satisfy the strong exception handling guarantee and should be rethought anyway.

Omnifarious
A: 

It looks as if you need some interface for client code, and some 'convenient' functionality for implementors of the interface, which they can only use if they follow the rule of calling the useThis function of the convenience layer, which will tweak their private members.

Whenever I gave in to the temptation of putting the convenience functionality in my abstract base class, I regretted it (soon!) afterwards. It takes away a lot of flexibility. The solution proposed by AlexKR makes this situation slightly better.

An alternative way of doing this is providing some convenience class that implementers of the interface can aggregate instead of inheriting it. It can provide a function taking the implementer's members as arguments.

class Interface { public: virtual void f() = 0; };

class Convenience { 
   public: 
   void tweakMyMembers( int& member1, float& member2 ); 
   bool somestate;
};

class Implementor : public Interface {
   int m1; float m2;
   public: Implementor( bool b ): conv( b ) {}

   virtual void f() { conv.tweakMyMembers( m1, m2 ); if( m1<m2 ) dothis(); }
};
xtofl
I take it that the Implementor class is meant to have a member variable named conv, that is a Convenience object?
Jeremy Friesner
@Jeremy: if the convenience needs state, the Implementor needs to aggregate it. Otherwise, the convenience may even be a set of functions.
xtofl