Reputation: 2593
I have a base class as interface and multiple derived classes. I have 2 util functions which needs to be used by each derived class methods. So I cannot put util functions in derived classes as it will be duplication of code. So one option is I can put them into separate util namespace, but I do not want it to expose to clients. So these methods needs to be hidden somewhere. What should be my design or how should I use these functions?
Upvotes: 1
Views: 151
Reputation: 7873
Some people want interface base classes to be empty pure virtual abstract classes, and this probably feels like "proper design" to them, but that level of abstraction leads to more inefficiencies in the implementation itself. But if you really want to keep your interface class totally abstract, just add another layer on top of that from which all implementations then derive:
Abstract pure virtual interface base class
^
|
|
Implementation base class: common things, including your utility functions
^ ^ ^
| | |
| | |
Impl1 Impl2 Impl3
-------- ...in code: --------
class IThing
{
public:
virtual void runAround();
virtual void makeAMess();
};
class ThingBase : public IThing
{
public:
// runAround() is not implemented here; implement it in derived classes
virtual void makeAMess()
{
// ...default implementation of makeAMess(), which a derived class
// can override if necessary...
}
private:
int utilFunc1(int a) { return ...whatever...; } // not virtual! (or, it could be, if necessary)
int utilFunc2(int a) { return ...whatever...; } // not virtual! (or, it could be, if necessary)
}
class Thing1 : public ThingBase
{
public:
virtual void runAround() { /* ...special code for Thing1... */ }
// Thing1 uses the default implementation of makeAMess()
}
class Thing2 : public ThingBase
{
public:
virtual void runAround() { /* ...special code for Thing2... */ }
// Thing2 uses the default implementation of makeAMess()
}
class Thing3 : public ThingBase
{
public:
virtual void runAround() { /* ...special code for Thing3... */ }
virtual void makeAMess() { /* ...special code for Thing3... */ }
}
If there is any common data or functionality between all implementations, by all means, move it to the implementation base class above. Keep it out of each derived implementation. But if that functionality (methods) are implementations of interface methods, then you're unnecessarily burdening all users with the (small) overhead of a polymorphic method invocation in order to call it:
Every derivation's vtable will have an extra function pointer for that common function, which has to be called polymorphically when called from interface-pointers/references even though there is only one implementation of that method. Every such invocation will take an extra two or three instructions, and an extra memory fetch, because it's polymorphic. The extra overhead isn't normally considered to be too much, but it is extra overhead, and in some situations it can be significant (e.g. a polymorphic operator[]()
for an "Array" class, when used thousands of times in a tight loop: If all derivations will have a private
pointer to wherever its data is allocated, then you could just put that pointer in the base class & implement a single non-polymorphic version of operator[]()
that accesses the appropriate element based on that pointer).
If due to the nature of your type, some functionality (and its associated data) will always be common across all derivations of it, the lower-overhead thing to do is to abandon the ideal of making your interface class totally abstract, and move that common functionality (and its associated data) into the interface class (now not totally abstract), and don't make the methods virtual
. I know that grates some people's nerves, but really, why pay the cost of polymorphism if you don't need to? You can always refactor it later if it turns out that some derivation actually does need different functionality.
Not-fully-abstract "interface" base class
^ ^ ^
| | |
| | |
Impl1 Impl2 Impl3
Note that if you do that, you can still have derivation-specific behavior in those non-polymorphic methods, because the non-polymorphic methods can, if necessary, call other class methods that are polymorphic.
Upvotes: 2