Some thoughts about virtual methods [modified]
-
I have this situation and I want to know the opinions of other members on this. My language is C++. There is a base class that has the most vital virtual function called
Job
. Within the derived classes, there are several possibilities of things to be done beforeJob
and/or after it. TheJob
would have to be done for about a hundred times* (The design has no problem, so if this sounds absurd it is only because I am not providing the exact details of the class). Also, this is supposed to run on multiple platforms, including .NET, mobile etc and not just native code on big fast CPUs. The core that contains this will remain in C++ and the necessary wrapping etc will be done to get it to run on the said platforms. In this scenario, considering speed and other things, which would be a better solution? 1. Have just theJob
function and let the derived class do something like thisDerivedClass::Job()
{
// pre operations
// Base::Job() or the override
// post operations
}2. Have three virtual functions,
PreJob, Job, PostJob
and have the call toJob
be bracketed like the following so that the code is neatly segregatedJobCaller()
{
pBaseClass->PreJob()
pBaseClass->Job()
pBaseClass->PostJob()
}EDIT: *about hundred for user interaction events like mouse clicks, screen taps etc depending on the platform. Basically, the user may "test" the load by playing with it.
...byte till it megahertz... my donation to web rubbish
modified on Thursday, December 23, 2010 8:02 AM
-
I have this situation and I want to know the opinions of other members on this. My language is C++. There is a base class that has the most vital virtual function called
Job
. Within the derived classes, there are several possibilities of things to be done beforeJob
and/or after it. TheJob
would have to be done for about a hundred times* (The design has no problem, so if this sounds absurd it is only because I am not providing the exact details of the class). Also, this is supposed to run on multiple platforms, including .NET, mobile etc and not just native code on big fast CPUs. The core that contains this will remain in C++ and the necessary wrapping etc will be done to get it to run on the said platforms. In this scenario, considering speed and other things, which would be a better solution? 1. Have just theJob
function and let the derived class do something like thisDerivedClass::Job()
{
// pre operations
// Base::Job() or the override
// post operations
}2. Have three virtual functions,
PreJob, Job, PostJob
and have the call toJob
be bracketed like the following so that the code is neatly segregatedJobCaller()
{
pBaseClass->PreJob()
pBaseClass->Job()
pBaseClass->PostJob()
}EDIT: *about hundred for user interaction events like mouse clicks, screen taps etc depending on the platform. Basically, the user may "test" the load by playing with it.
...byte till it megahertz... my donation to web rubbish
modified on Thursday, December 23, 2010 8:02 AM
(2) is the cleaner approach, obviously. if PreJob+Job+PostJob is more than 100 lines of code (dynamically speaking), then the difference in performance will be unnoticeable. if mosr jobs have different PreJob and PostJob functionality (never shared with the base class), the added structure doesn't bring you much. So look at the statistics and decide for yourself. :)
Luc Pattyn [Forum Guidelines] [Why QA sucks] [My Articles] Nil Volentibus Arduum
Please use <PRE> tags for code snippets, they preserve indentation, improve readability, and make me actually look at the code.
-
I have this situation and I want to know the opinions of other members on this. My language is C++. There is a base class that has the most vital virtual function called
Job
. Within the derived classes, there are several possibilities of things to be done beforeJob
and/or after it. TheJob
would have to be done for about a hundred times* (The design has no problem, so if this sounds absurd it is only because I am not providing the exact details of the class). Also, this is supposed to run on multiple platforms, including .NET, mobile etc and not just native code on big fast CPUs. The core that contains this will remain in C++ and the necessary wrapping etc will be done to get it to run on the said platforms. In this scenario, considering speed and other things, which would be a better solution? 1. Have just theJob
function and let the derived class do something like thisDerivedClass::Job()
{
// pre operations
// Base::Job() or the override
// post operations
}2. Have three virtual functions,
PreJob, Job, PostJob
and have the call toJob
be bracketed like the following so that the code is neatly segregatedJobCaller()
{
pBaseClass->PreJob()
pBaseClass->Job()
pBaseClass->PostJob()
}EDIT: *about hundred for user interaction events like mouse clicks, screen taps etc depending on the platform. Basically, the user may "test" the load by playing with it.
...byte till it megahertz... my donation to web rubbish
modified on Thursday, December 23, 2010 8:02 AM
Unless the optimizer has changed much, if PreJob and PostJob don't do anything in a class, I believe they are optimized out. It's been a long time since I've done any serious C++, but I seem to remember that this is the case. As the base class is only going to be using VTable lookups here anyway, the overhead should not be significant in real terms.
I'm not a stalker, I just know things. Oh by the way, you're out of milk.
Forgive your enemies - it messes with their heads
-
(2) is the cleaner approach, obviously. if PreJob+Job+PostJob is more than 100 lines of code (dynamically speaking), then the difference in performance will be unnoticeable. if mosr jobs have different PreJob and PostJob functionality (never shared with the base class), the added structure doesn't bring you much. So look at the statistics and decide for yourself. :)
Luc Pattyn [Forum Guidelines] [Why QA sucks] [My Articles] Nil Volentibus Arduum
Please use <PRE> tags for code snippets, they preserve indentation, improve readability, and make me actually look at the code.
I agree with Luc on performance issues, but I disagree when he say that the added structure would not be beneficial. If your core Job function cannot stand on its own (i.e., requires pre- and post- function, regardless of how different those functions may be from derived class to derived class), then I personally would absolutely declare the Pre- and Post- as "abstract" methods to ensure that every new derived class you create implements Pre- and Post-. This is exactly why "abstract" declarations were invented. You could then invoke Pre- and Post- from Base::Job and not have to implement a Derived::Job method at all. Like this (I apologize if my syntax isn't perfect. Been doing a lot of C++/CLI, so I might not be completely right in native C++ syntax):
class Base
{
public:
virtual void Job()
{
Pre();
// actual job code here
Post();
}
protected:
virtual void Pre() abstract;
virtual void Post() abstract;
}class Derived : public Base
{
protected:
virtual void Pre() override
{
//derived-specific pre-job code here
}virtual void Post() override
{
//derived-specific post-job code here
}
}// Calling Code:
Derived* myDerived = new Derived();
myDerived->Job(); // invokes Job in base class, which in turn invokes Pre/Post in derived class. -
I agree with Luc on performance issues, but I disagree when he say that the added structure would not be beneficial. If your core Job function cannot stand on its own (i.e., requires pre- and post- function, regardless of how different those functions may be from derived class to derived class), then I personally would absolutely declare the Pre- and Post- as "abstract" methods to ensure that every new derived class you create implements Pre- and Post-. This is exactly why "abstract" declarations were invented. You could then invoke Pre- and Post- from Base::Job and not have to implement a Derived::Job method at all. Like this (I apologize if my syntax isn't perfect. Been doing a lot of C++/CLI, so I might not be completely right in native C++ syntax):
class Base
{
public:
virtual void Job()
{
Pre();
// actual job code here
Post();
}
protected:
virtual void Pre() abstract;
virtual void Post() abstract;
}class Derived : public Base
{
protected:
virtual void Pre() override
{
//derived-specific pre-job code here
}virtual void Post() override
{
//derived-specific post-job code here
}
}// Calling Code:
Derived* myDerived = new Derived();
myDerived->Job(); // invokes Job in base class, which in turn invokes Pre/Post in derived class.I would also add a
protected virtual void Actual()
containing the actual job code, and call that from the
Job()
method. Not only does it make things clearer, you can also be sure that the actual job code will have to be overridden in a derived class soon. -
I have this situation and I want to know the opinions of other members on this. My language is C++. There is a base class that has the most vital virtual function called
Job
. Within the derived classes, there are several possibilities of things to be done beforeJob
and/or after it. TheJob
would have to be done for about a hundred times* (The design has no problem, so if this sounds absurd it is only because I am not providing the exact details of the class). Also, this is supposed to run on multiple platforms, including .NET, mobile etc and not just native code on big fast CPUs. The core that contains this will remain in C++ and the necessary wrapping etc will be done to get it to run on the said platforms. In this scenario, considering speed and other things, which would be a better solution? 1. Have just theJob
function and let the derived class do something like thisDerivedClass::Job()
{
// pre operations
// Base::Job() or the override
// post operations
}2. Have three virtual functions,
PreJob, Job, PostJob
and have the call toJob
be bracketed like the following so that the code is neatly segregatedJobCaller()
{
pBaseClass->PreJob()
pBaseClass->Job()
pBaseClass->PostJob()
}EDIT: *about hundred for user interaction events like mouse clicks, screen taps etc depending on the platform. Basically, the user may "test" the load by playing with it.
...byte till it megahertz... my donation to web rubbish
modified on Thursday, December 23, 2010 8:02 AM
Have you looked at Aspect Oriented Programming (AOP)? It provides a great alternative to inheritance in cases like yours. Don't know how complicated that triplet(?) of
Pre
,Actual
, andPost
calls will become, but my experience tells me that the orchestration of overridden virtual calls of that kind quickly becomes a nightmare... A good half-way to AOP are design patterns like chain of responsibility - in many cases this gives you larger flexibility and better control over the behaviour than (often hacky) orchestration of calls to overridden methods. By the way, if you are on native C++, have a look at the C++ idiom calledWrap
http://www2.research.att.com/~bs/wrapper.pdf - an interesting possibility to get compile time configurable Aspect-like behaviour. Often (but not always) the flexibility supersedes the one of polymorphic inheritance based implementation, could have issues about interoperability though... Cheers, Paul -
I have this situation and I want to know the opinions of other members on this. My language is C++. There is a base class that has the most vital virtual function called
Job
. Within the derived classes, there are several possibilities of things to be done beforeJob
and/or after it. TheJob
would have to be done for about a hundred times* (The design has no problem, so if this sounds absurd it is only because I am not providing the exact details of the class). Also, this is supposed to run on multiple platforms, including .NET, mobile etc and not just native code on big fast CPUs. The core that contains this will remain in C++ and the necessary wrapping etc will be done to get it to run on the said platforms. In this scenario, considering speed and other things, which would be a better solution? 1. Have just theJob
function and let the derived class do something like thisDerivedClass::Job()
{
// pre operations
// Base::Job() or the override
// post operations
}2. Have three virtual functions,
PreJob, Job, PostJob
and have the call toJob
be bracketed like the following so that the code is neatly segregatedJobCaller()
{
pBaseClass->PreJob()
pBaseClass->Job()
pBaseClass->PostJob()
}EDIT: *about hundred for user interaction events like mouse clicks, screen taps etc depending on the platform. Basically, the user may "test" the load by playing with it.
...byte till it megahertz... my donation to web rubbish
modified on Thursday, December 23, 2010 8:02 AM
From a perfectionists' point of view, having job callers deal with sequences of calls violates encapsulation: job callers do not need to know that a job requires a "setup" and a "cleanup" steps. To hide the internals from your callers while keeping the distinction in your implementations, make an abstract class with a skeleton method, then override pre/post/job methods as needed in your derived classes. This technique is commonly known as the "Template Method" pattern[^]; I prefer "skeleton" to avoid confusion with C++ templates.
AbstractClass::Job()
{
doPreJob();
doJob();
doPostJob();
}DerivedClass::doPreJob() {}
DerivedClass::doJob() {}
DerivedClass::doPostJob() {}
JobCaller()
{
pAbstractClass -> Job();
}