most elegant solution - empty virtual funciton, rtti or static data member?

M

Mark Turney

Problem:
I have a vector full of two different derived class objects (class B
and class C) that are derived from the same base class A. I want to
loop through vector and invoke a member function in only objects of
class B and skip over the objects of class C. To complicate things,
I'm using the vector position (index) as an argument in the invoked
member function. It is possible to move the position into the object
by adding a data member, but that feels like duplicating data when I'm
trying to keep things streamlined. I also think that needing to use
the vector index pretty much kills sorting or other use of iterators
or separate vectors.

The possible solutions that I have thought of are:
1. Calling a common virtual function of each object through a base
class pointer, while leaving the virtual function for class C (the
objects to skip) empty. Cons - Empty member functions seem ugly.
2. Using run-time type identification (typeid() == typid()) and a
series of if () else statments to skip the objects of class C. Cons -
The ensuing if-else tree feels like a botched switch statement.
3. Creating a static data member in each class that holds an int that
corresponds to the class type. Then, using this value in a switch
statement to skip over the objects of class C. Cons - I like the
switch in this step as opposed to solution 2, but it feels wrong
duplicating type information.

Am I missing something obvious, or if I'm moving in the right
direction, which of the solutions is the most elegant? If you're
feeling especially generous I would love to be told why any of my
ideas suck and why the best idea is the best.

Thanks for any help,
-Mark
 
V

Victor Bazarov

Mark said:
Problem:
I have a vector full of two different derived class objects (class B
and class C) that are derived from the same base class A. I want to
loop through vector and invoke a member function in only objects of
class B and skip over the objects of class C. To complicate things,
I'm using the vector position (index) as an argument in the invoked
member function. It is possible to move the position into the object
by adding a data member, but that feels like duplicating data when I'm
trying to keep things streamlined. I also think that needing to use
the vector index pretty much kills sorting or other use of iterators
or separate vectors.

The possible solutions that I have thought of are:
1. Calling a common virtual function of each object through a base
class pointer, while leaving the virtual function for class C (the
objects to skip) empty. Cons - Empty member functions seem ugly.

Get over it. Your class A should have that function empty and not
pure. Class B will redefine it by overriding. Class C won't.
2. Using run-time type identification (typeid() == typid()) and a
series of if () else statments to skip the objects of class C. Cons -
The ensuing if-else tree feels like a botched switch statement.

That's definitely true.
3. Creating a static data member in each class that holds an int that
corresponds to the class type. Then, using this value in a switch
statement to skip over the objects of class C. Cons - I like the
switch in this step as opposed to solution 2, but it feels wrong
duplicating type information.

Not only that, but then what if you add class 'D' to the mix? Now
you have to remember to implement that nonsense in it as well.
Am I missing something obvious, or if I'm moving in the right
direction, which of the solutions is the most elegant? If you're
feeling especially generous I would love to be told why any of my
ideas suck and why the best idea is the best.

Since you imply polymorphism when you store pointers to objects
of two different types in the same container, you should stick to
your convictions and use polymorphism wherever possible. Going
off and re-implementing RTTI by whatever means may be tempting,
but trust me, it's not a good idea.

Victor
 
M

Marcin Kalicinski

Uzytkownik "Victor Bazarov said:
Get over it. Your class A should have that function empty and not
pure. Class B will redefine it by overriding. Class C won't.


That's definitely true.


Not only that, but then what if you add class 'D' to the mix? Now
you have to remember to implement that nonsense in it as well.


Since you imply polymorphism when you store pointers to objects
of two different types in the same container, you should stick to
your convictions and use polymorphism wherever possible. Going
off and re-implementing RTTI by whatever means may be tempting,
but trust me, it's not a good idea.

If your hierarchy has only 2 "types" of classes, ones that should be checked
and ones that should not, you could consider having 2 vectors that store
them separately. If possible, it's always better to use compile-time
facilities than runtime.

Best regards,
Marcin
 
K

Kevin Saff

Mark Turney said:
Problem:
I have a vector full of two different derived class objects (class B
and class C) that are derived from the same base class A. I want to
loop through vector and invoke a member function in only objects of
class B and skip over the objects of class C. To complicate things,
I'm using the vector position (index) as an argument in the invoked
member function. It is possible to move the position into the object
by adding a data member, but that feels like duplicating data when I'm
trying to keep things streamlined. I also think that needing to use
the vector index pretty much kills sorting or other use of iterators
or separate vectors.

I think the solution to the indexing problem depends on what the index means
in your problem. I don't see why you can't use iterators, though; I
sometimes use a separate unsigned variable that I increment as I loop
through a container. If you know you are using a vector, then you can use
pointer arithmetic, getting the index by subtracting the begin iterator from
your current position.
The possible solutions that I have thought of are:
1. Calling a common virtual function of each object through a base
class pointer, while leaving the virtual function for class C (the
objects to skip) empty. Cons - Empty member functions seem ugly.

Why do you think this is ugly? It's the standard OO solution to this
problem. If you start seeing that class C has frequent exceptions like
this, you might reconsider the choice to derive from A, since it cannot
support the full interface. However, if it's an operation that makes sense
to ignore, I don't see the problem. (In fact, if it's a function you expect
only certain derived classes to handle, you could make the implementation
empty in A and it won't even show up in C.)
2. Using run-time type identification (typeid() == typid()) and a
series of if () else statments to skip the objects of class C. Cons -
The ensuing if-else tree feels like a botched switch statement.

It's worse than a botched switch statement, you're doing your own virtual
lookup instead of using the language's built in lookup. Think of it this
way: when a virtual function is called, the program (somehow) decides what
derived function to call from the class's type. Why do you suppose doing
this manually is better than using the built-in feature? Surely, if the
compiler is worth using, it will solve this problem more optimally than you
can. The goal of OO languages was to eliminate this kind of manual type
dispatching.
3. Creating a static data member in each class that holds an int that
corresponds to the class type. Then, using this value in a switch
statement to skip over the objects of class C. Cons - I like the
switch in this step as opposed to solution 2, but it feels wrong
duplicating type information.

This has the cons of #2 with some new ones. Notice solutions like this
usually need both a static member of each class to identify the class, and a
virtual function to identify the run-time object. They will need different
names, since C++ doesn't allow virtual and static functions to share a name.
So, this solution requires 3 functions, two of which aren't really related
to the problem at hand; this can contribute to
documentation/understandability issues later on.
Am I missing something obvious, or if I'm moving in the right
direction, which of the solutions is the most elegant? If you're
feeling especially generous I would love to be told why any of my
ideas suck and why the best idea is the best.

#1 is the best. I hope I have explained why.
Thanks for any help,
-Mark

HTH
 

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

No members online now.

Forum statistics

Threads
473,769
Messages
2,569,576
Members
45,054
Latest member
LucyCarper

Latest Threads

Top