Markus Svilans wrote:
Salt_Peter wrote:
Markus Svilans wrote:
[ snip ]
Instead of void* data, why don't you template the class. You'll freak
out over the enormous amount of code you'll save and that buffer could
then be used to store anything. Anything including your own user-types.
Add to that the fact that the compiler's type checking ability is not
disabled. Programming with void* data is like walking around
blind-folded.
(see template below).
Good analogy! Thanks for the suggestion to use a template for the data
buffers. It's a very good idea. A fine example of great advice on
comp.lang.c++.
In each of the cases, #1 and #2, which version of the HandleBuffer()
method in the ProcessorDerived object gets called?
>
(My reasoning says that in both cases, HandleBuffer(BufferBase &buffer)
will be called, because the method is being invoked through a base
class pointer.)
The second virtual member function
void ProcessorDerived::HandleBuffer(BufferDerived &buffer) { ... }
is not needed
You need to make ProcessorBase's destructor virtual.
Another good tip. I do this in my real code, but for the example code I
chose not to show any destructors.
Modify the
void ProcessorDerived::HandleBuffer(BufferBase &buffer)
as follows:
...
#include <typeinfo>
class ProcessorDerived : public ProcessorBase
{
// ...
public:
virtual void HandleBuffer(BufferBase &buffer)
{
std::cout << "ProcessorDerived::HandleBuffer(BufferBase
&buffer)\n";
std::cout << "buffer's type is " << typeid(buffer).name();
std::cout << std::endl;
}
};
I gather then that using RTTI is the only way to go here.
no, i just used typeid to show you that the reference is actually
pointing to a derived object with no need to cast anything. References,
not just pointers, work nicely with polymorphic objects.
In the event that you have some virtual member function in BufferBase
and its derivatives, these will get called polymorphicly and
transparently in void HandleBuffer(BufferBase &buffer).
>
Are there any good articles available online that outline the
performance penalties (if any) incurred when using RTTI?
There is no real need to use RTTI here. Polymorphism is what you need.
>
The HandleBuffer() method may end up being called very frequently
(100's or 1000's of times per second) so I would like to make the
dispatching as fast as possible.
Contrary to popular opinion, in the situation you've described, there
is a cost in not using polymorphism. Specially when the cost includes
having to write the code. The cost of virtual calls themselves is
often, not always, a performance enhancement. You'ld have to compare.
>
___
Now, for the template:
#include <iostream>
#include <ostream>
#include <vector>
template< typename T >
class BufferBase
{
Thanks for sharing the template code.
At the beginning, when I started working on this code, I opted not to
use a std::vector based implementation. Instead I chose to use my own
managed array class, because I needed access to contiguous elements in
memory (mainly for interoperability with C API functions).
Another article on this newsgroup brought my attention to this FAQ:
http://www.parashift.com/c++-faq-lit....html#faq-34.3
Because std::vector does allow seamless integration with C functions,
which require a pointer to a contiguous array of objects, I'm now going
to switch to an std::vector based implementation.
You've done well to research it. A std::vector is a very capable
container. Its dynamic, copyable, fast and opens the door to a slew of
algorithms you can throw at it. Its only weakness is dynamic
reallocation which is easily fixed with an appropriate resize().
Building your *own* container class with a std::vector member is easy,
safe and simple. Including with iterator support. As you go along,
you'll find yourself designing your container classes using
std::vector's own inteface, its quite well designed.
With many std::vector implementations, you'll find they are actually
faster than a primitive array by about 5% to 10% and more if the
dataset is sizeable. You won't see the speed benefit until you compile
in release mode (not debug).
What is nice is being able to use const references, references, stream
its elements or overload operators, etc. It also has a range_checking
member function at(...). The list goes on and on.
example with the proverbial templated op<<...
#include <iostream>
#include <ostream>
#include <vector>
template< typename T >
std::ostream&
operator<<(std::ostream& os, std::vector< T >& vt)
{
typedef typename std::vector< T >::iterator VIter;
for( VIter viter = vt.begin(); viter != vt.end(); ++viter )
{
os << *viter;
if(viter != --vt.end()) os << ", ";
}
return os;
}
int main()
{
std::vector< double vd(10, 11.1);
std::cout << vd << std::endl;
std::vector< char vc(10, 'a');
vc.push_back('b');
std::cout << vc << std::endl;
std/::vector< char vc2( vc ); // 11 chars copied
}
/*
11.1, 11.1, 11.1, 11.1, 11.1, 11.1, 11.1, 11.1, 11.1, 11.1
a, a, a, a, a, a, a, a, a, a, b
*/