Virtual function and inheritance question

M

Markus Svilans

Hello,

My question involves virtual functions and inheritance.

Suppose we have a class structure, that consists of "data" classes, and
"processor" classes. The data classes are derived from BufferBase, and
customized in order to be able to a type of data (of any kind, such as
chunks of audio samples from a sound card). The processor classes are
derived from ProcessorBase, and are customized to handle
BufferBase-derived objects. Each BufferBase-derived class has a
corresponding ProcessorBase-derived class.

The following code summarizes this:

//=================
// Base classes

class BufferBase
{
public:
// Some arbitrary method for putting data into the buffer.
virtual void StoreData(void *data, int size);
};

class ProcessorBase
{
public:
// Virtual method, which derived classes implement to
// handle incoming data.
virtual void HandleBuffer(BufferBase &buffer) = 0;
};

//=================
// Derived classes

class BufferDerived : public BufferBase
{
// custom functions and data
};

// Class to handle BufferDerived objects
class ProcessorDerived : public ProcessorBase
{
public:
// Implement the pure virtual method in the base class
virtual void HandleBuffer(BufferBase &buffer)
{
// process data buffer
}

// Override the virtual method
virtual void HandleBuffer(BufferDerived &buffer)
{
// process data buffer
}
};


My question then is, if the following code is executed:

BufferBase buffer1;
BufferDerived buffer2;

// We will access the ProcessorDerived object through a
// base class pointer.
ProcessorBase *processor = new ProcessorDerived;

processor->HandleBuffer(buffer1); // Case #1
processor->HandleBuffer(buffer2); // Case #2

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.)

Regards,
Markus.
 
S

Salt_Peter

Markus said:
Hello,

My question involves virtual functions and inheritance.

Suppose we have a class structure, that consists of "data" classes, and
"processor" classes. The data classes are derived from BufferBase, and
customized in order to be able to a type of data (of any kind, such as
chunks of audio samples from a sound card). The processor classes are
derived from ProcessorBase, and are customized to handle
BufferBase-derived objects. Each BufferBase-derived class has a
corresponding ProcessorBase-derived class.

The following code summarizes this:

//=================
// Base classes

class BufferBase
{
public:
// Some arbitrary method for putting data into the buffer.
virtual void StoreData(void *data, int size);
};

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).
class ProcessorBase
{
public:
// Virtual method, which derived classes implement to
// handle incoming data.
virtual void HandleBuffer(BufferBase &buffer) = 0;
};

//=================
// Derived classes

class BufferDerived : public BufferBase
{
// custom functions and data
};

// Class to handle BufferDerived objects
class ProcessorDerived : public ProcessorBase
{
public:
// Implement the pure virtual method in the base class
virtual void HandleBuffer(BufferBase &buffer)
{
// process data buffer
}

// Override the virtual method
virtual void HandleBuffer(BufferDerived &buffer)
{
// process data buffer
}
};


My question then is, if the following code is executed:

BufferBase buffer1;
BufferDerived buffer2;

// We will access the ProcessorDerived object through a
// base class pointer.
ProcessorBase *processor = new ProcessorDerived;

processor->HandleBuffer(buffer1); // Case #1
processor->HandleBuffer(buffer2); // Case #2

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.
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;
}
};

and you'll get something like:
ProcessorDerived::HandleBuffer(BufferBase &buffer)
buffer's type is BufferBase
ProcessorDerived::HandleBuffer(BufferBase &buffer)
buffer's type is BufferDerived // <- note !!!

You don't need the second function taking a BufferDerived parameter.

___
Now, for the template:

#include <iostream>
#include <ostream>
#include <vector>

template< typename T >
class BufferBase
{
std::vector< T > vt;
public:
BufferBase(size_t sz = 0, const T& t = T())
: vt(sz, t) { }
BufferBase(const BufferBase& copy)
{
vt = copy.vt;
}
/* member functions */
size_t size() const { return vt.size(); }
/* friend op<< */
friend std::eek:stream&
operator<<(std::eek:stream& os, const BufferBase& r_bb)
{
typedef typename std::vector< T >::const_iterator VIter;
for(VIter viter = r_bb.vt.begin();
viter != r_bb.vt.end();
++viter)
{
os << *viter << " ";
}
return os;
}
};

int main()
{

BufferBase< int > buffer(10);
std::cout << "buffer size = " << buffer.size() << std::endl;
std::cout << buffer << std::endl;

BufferBase< char > cbuffer(10, 'a');
std::cout << "buffer size = " << cbuffer.size() << std::endl;
for(char c = 'b'; c < 'z'; ++c)
{
cbuffer.push_back(c);
}
std::cout << "buffer size = " << cbuffer.size() << std::endl;
std::cout << cbuffer << std::endl;

return 0;
}

/*
buffer size = 10
0 0 0 0 0 0 0 0 0 0
buffer size = 10
buffer size = 34
a a a a a a a a a a b c d e f g h i j k l m n o p q r s t u v w x y
*/
 
S

Sandip Shahane

ProcessorDerived class's function shall get invoked since the function
is declared as virtual in which case the message is passed based on the
type of class it is refering to rather than the type of the object
itself.
 
S

Salt_Peter

Sandip said:
ProcessorDerived class's function shall get invoked since the function
is declared as virtual in which case the message is passed based on the
type of class it is refering to rather than the type of the object
itself.

Don't top Post, please.
The fact that it is ProcessorDerived's function that gets called is
something the OP is already aware of.
The point was that the parameter reference is taken polymorphicly.
And the second function doesn't match the virtual function's signature
anyways.
 
M

Markus Svilans

Salt_Peter said:
Markus said:

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++.

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.

Are there any good articles available online that outline the
performance penalties (if any) incurred when using RTTI?

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.


___
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-lite/containers.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.

Regards,
Markus.
 
S

Salt_Peter

Markus said:
Salt_Peter said:
Markus said:

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++.

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.
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-lite/containers.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::eek:stream&
operator<<(std::eek:stream& 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
*/
 
M

Markus Svilans

Salt_Peter said:
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).

I see that.

However, in my original question I was wondering what happens if you
call a virtual method in the base class, that has been overridden in a
derived class. In the example code I showed, ProcessorDerived had two
methods:

HandleBuffer(BufferBase &buffer) // inherited from ProcessorBase
HandleBuffer(BufferDerived &buffer) // a new, overridden version in
ProcessorDerived

After some experimenting, it became clear that the inherited version is
always the one that is invoked, when calling through a base class
pointer (in this case through a ProcessorBase pointer). The overridden
version is only called when calling from a derived class pointer.
There is no real need to use RTTI here. Polymorphism is what you need.

This is true.

However given the class hierarchy described in my original message,
what I'm looking for is some safe way of passing BufferBase-derived
objects to the correct ProcessorBase-derived objects.

Imagine we had the following additional classes:

// Imaginary class that stores a chunk of audio from an audio stream
class AudioBuffer : public BufferBase
{
private:
int timestamp;
// ... other data members and methods ...
public:
// ... public methods, constructors and destructors
};

// Imaginary class that stores a frame from a video data stream
class VideoFrameBuffer : public BufferBase
{
private:
// The video frame stores in some Bitmap object
Bitmap frame;
public:
// ... public methods, constructors and destructors
};

class AudioProcessor : public ProcessorBase
{
public:
// Inherited from ProcessorBase
virtual void HandleBuffer(BufferBase &buffer);
};

What I'm really after is a fast and safe way of automatically ensuring
that the AudioProcessor::HandleBuffer() method can only be passed
AudioBuffer objects, and not VideoFrameBuffer objects, or other objects
derived from BufferBase.

For example, if somehow an VideoFrameBuffer object gets passed to an
AudioProcessor, it would be nice if an exception were automatically
thrown.

Currently, the only method that comes to my mind is to use
dynamic_cast, or other form of RTTI, in AudioProcessor::HandleBuffer to
ensure that the passed buffer is an AudioBuffer.

Any ideas for how do to this?

(I thought of using a templated virtual method in the base class, but
apparently you can't create virtual methods that are also templates.)

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.

I'm definitely a fan of polymorphism. I'm not trying to start any fires
when I say that in my opinion this is one of the great advantages of
C++ (and other OO languages) over C and other non-OO languages.

Regards,
Markus.
 
S

Salt_Peter

Markus said:
I see that.

However, in my original question I was wondering what happens if you
call a virtual method in the base class, that has been overridden in a
derived class. In the example code I showed, ProcessorDerived had two
methods:

HandleBuffer(BufferBase &buffer) // inherited from ProcessorBase
HandleBuffer(BufferDerived &buffer) // a new, overridden version in
ProcessorDerived

After some experimenting, it became clear that the inherited version is
always the one that is invoked, when calling through a base class
pointer (in this case through a ProcessorBase pointer). The overridden
version is only called when calling from a derived class pointer.


This is true.

However given the class hierarchy described in my original message,
what I'm looking for is some safe way of passing BufferBase-derived
objects to the correct ProcessorBase-derived objects.

Imagine we had the following additional classes:

// Imaginary class that stores a chunk of audio from an audio stream
class AudioBuffer : public BufferBase
{
private:
int timestamp;
// ... other data members and methods ...
public:
// ... public methods, constructors and destructors
};

// Imaginary class that stores a frame from a video data stream
class VideoFrameBuffer : public BufferBase
{
private:
// The video frame stores in some Bitmap object
Bitmap frame;
public:
// ... public methods, constructors and destructors
};

class AudioProcessor : public ProcessorBase
{
public:
// Inherited from ProcessorBase
virtual void HandleBuffer(BufferBase &buffer);
};

What I'm really after is a fast and safe way of automatically ensuring
that the AudioProcessor::HandleBuffer() method can only be passed
AudioBuffer objects, and not VideoFrameBuffer objects, or other objects
derived from BufferBase.

No problem, use templates.
The compiler has no choice but to create those versions of the
templates you use. It can't call the wrong buffer with a processor
because no virtual member function would exist to match the call.
No RTTI needed unless you plan to make soup.
Of course, that means you'll need to store Audio things seperate from
Video things (ie: seperate containers). Note that default template
parameters below means that < > must still be used to declare the
derived processors in main().

#include <iostream>
#include <ostream>
#include <vector>

class BufferBase {
public:
BufferBase() { }
virtual ~BufferBase() { }
};

class AudioBuffer : public BufferBase { };

class VideoBuffer : public BufferBase { };

template< typename BufferType >
class ProcessorBase {
public:
virtual ~ProcessorBase() { }
virtual void HandleBuffer(BufferType &buffer) = 0;
};

template< typename BufferType = AudioBuffer >
class AudioProcessor : public ProcessorBase< BufferType > {
public:
virtual void HandleBuffer(BufferType &buffer) {
std::cout << "AudioProcessor::HandleBuffer(...)\n";
}
};

template< typename BufferType = VideoBuffer >
class VideoProcessor : public ProcessorBase< BufferType > {
public:
virtual void HandleBuffer(BufferType &buffer) {
std::cout << "VideoProcessor::HandleBuffer(...)\n";
}
};

int main() {
VideoBuffer vidbuf;
VideoProcessor< > vidproc;
vidproc.HandleBuffer( vidbuf );

AudioBuffer audiobuf;
AudioProcessor< > audioproc;
audioproc.HandleBuffer( audiobuf );

// test
// audioproc.HandleBuffer( vidbuf ); // error: no matching call
}

If you uncomment the error, the compiler doesn't even let you compile
the program.
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

Forum statistics

Threads
473,994
Messages
2,570,223
Members
46,810
Latest member
Kassie0918

Latest Threads

Top