The Design of Software (CLOSED)

A public forum for discussing the design of software, from the user interface to the code architecture. Now closed.

The "Design of Software" discussion group has been merged with the main Joel on Software discussion group.

The archives will remain online indefinitely.

C++ OOP Question:  This has to be possible

Hi,

I am positive I have done this before and cannot think why it would be uncommon.  I am trying to call a derived method from a base class method.  The derived method is declared virtual in the base class.  Consider:
-------------------------------------------
class Base
{
protected:
    virtual void _basefunc();
public:
    void doBaseFunc();
}

class derived1 : public Base
{
protected:
    void _basefunc();
}

class derived2 : public Base
{
protected:
    void _basefunc();
}
/////
//Implementation of Base->doBaseFunc
/////
void Base::doBaseFunc()
{
    _basefunc();
}


--------------------------------------
Now, I want to instantiate a variable as derived1 and another as derived2.  Since I store these in a PtrArray, I cast them as base class when making a call to generic doBaseFunc(). 
Since they were instantiated as Derived classes, I thought this would work.  Of course what is happening is the base class _baseFunc is being called instead of the overridden method in derived classes.


Sorry for the long post.  Since it is Sunday night and this is driving me nuts, I thought someone on the board might have an idea.  Feel free to flame away if this is either dumb or other :).

-Dan
Help please....Oh please help :)
Sunday, September 24, 2006
 
 
> Since I store these in a PtrArray, I cast them as base class when making a call to generic doBaseFunc().

It sounds like you're suffering from what's called "object slicing": to fix it  you should define your PtrArray to contain pointers to the "Base" (or derived) instances, instead of containing only (object-sliced) "Base" instances.
Christopher Wells Send private email
Sunday, September 24, 2006
 
 
Interesting.  I have never heard that phrase.  One thing I didn't mention but may be relevant to 'object slicing' is that the derived classes all use the base class constructor.  This seems related to slicing but haven't narrowed down why yet.

Your other piece of advice I am not as clear on.  I instantiate the derived classes as the derived class and add them to my ptrarray.  When I reference them (in a for loop for example), they need to be cast as something.  Since I don't which of the derived type each instance may be, I use the base class as the cast.  Does that make sense?

Also, creating constructors for each derived class didnt help as one item about object slicing had mentioned.

Thanks for that speedy response.

-Dan
Help please....Oh please help :)
Sunday, September 24, 2006
 
 
Object slicing:

Base base;
base.doBaseFunc(); //ok: calls Base function

Derived derived;
derived.doBaseFunc(); //ok: calls Derived function

//wrong way:

Base b1 = derived;
b1.doBaseFunc(); //oops: calls Base function!

//right way:

Base* b2 = &derived;
b2->doBaseFunc(); //ok: calls Derived function

---------

I don't know what "PtrArray" is but I'm guessing you're using it to do the equivalent of ...

Base stuff[] = new Base[2];
Derived d1;
stuff[0] = (Base) d1;
... etc ...
stuff[0].doBaseFunc(); //oops: calls Base function!

... an that instead you should use it to do the equivalent of ...

Base* stuff[] = new Base*[2];
Derived d1 = new Derived();
stuff[0] = d1; //no cast required
... etc ...
stuff[0]->doBaseFunc(); //ok: calls Derived function

... only now that Derived instances are on the heap, don't forget to delete them when you have finished with them ...

delete stuff[0];
Christopher Wells Send private email
Sunday, September 24, 2006
 
 
Hi,

Yes, I think what you have described would work.  I guess I am just confused on how I had done this before.  It may have been in Delphi using the explicit 'override' directive. 

The PtrArray I was referrring to is an MFC class called CPtrArray.  It is simply a wrapper for an unbounded array of void pointers with easy methods like add,insert, etc.  The catch is that since the pointers are stored void, you need to cast.  Your scenario would solve the casting issue for sure.
Help please....Oh please help :)
Sunday, September 24, 2006
 
 
I don't know what's going wrong then (since you didn't post a complete set of source): in any case you should have no casts to (Base) in your code, but casts to (Base*) are ok though.

FWIW I never use the MFC collection templates; they predate the C++ STL collection templates, which are more type-safe etc. and which I prefer: here I'd prefer something like the "std::vector<Base*>" class instead of the CPtrArray class.
Christopher Wells Send private email
Sunday, September 24, 2006
 
 
Hi,

Thanks for your insight. I am reasonably sure that the cast to base is the issue; it is base* but even still, that seems like the obvious culprit.  I guess the core of my thinking was that since i was overriding the base class method, the derived method would be executed even when cast as Base.  As I mentioned, I am positive I had done this before but I am leaning now that it was in Delphi using similar object to CPtrArray (TList).  In that case, I believe the additional option that was required was to declare a method as 'override' in its definition.

In any event, thanks for your help.  I will take a look at STL.

-Dan
Help please....Oh please help :)
Sunday, September 24, 2006
 
 
CPtrArray is storing pointers so I don't see it causing a problem.

My next guess is that you're passing the ojects by value to a subroutine, and thus sliced them then, for example ...

class Foo
{
  void do(Base b) { b.doBaseFunc(); }
};

... in whch case the fix would be to pass by pointer, like ...

void do(Base* b) { b->doBaseFunc(); }

... or to pass by reference, like ...

void do(Base& b) { b.doBaseFunc(); }
Christopher Wells Send private email
Sunday, September 24, 2006
 
 
You forgot to define _basefunc() as virtual in your derived classes.
Omer Send private email
Monday, September 25, 2006
 
 
C++ doesn't require that: only that the method in the derived class has the same name as a virtual method in a base class.
Christopher Wells Send private email
Monday, September 25, 2006
 
 
I see two reasons for your symptoms:
- object slicing which is very unlikely as you are instanciating and pushing pointers into your array of pointers...
- the call to your virtual function is either from a constructor or from the destructor of your object. The only workaround, in C++, is to isolate a specialized post-construction (/ pre-destruction) phase ; or to rethink your design, which is not always possible. Having a post-construction phase is easy to implement when we use factories.

PS: "virtual" is only mandatory in the declaration at base class level.
Luc Hermitte Send private email
Monday, September 25, 2006
 
 
> has the same name

To be nitpicking, the same signature. It's important at times to remember (making it possible to create a new non-intended non-virtually function by accident)
A hint for the OP - better don't use names with leading underscores. Not being prohibited in a strict sense, they are still reserved for C++ implementation.

Micha
Micha Send private email
Monday, September 25, 2006
 
 
Chris and Luc, I stand corrected. I guess one of the other languages that I have used over the years (pre-Delphi Turbo Pascal?) confused me into thinking it was required.

I agree that object slicing seems like the most likely explanation. What helps me in cases like this is to print debugging information from all constructors, copy constructors, destructors and assignment operators so you get some idea of what is happening under the covers.
Omer Send private email
Monday, September 25, 2006
 
 
std::vector<Base*> is not brilliant because it loses you all the benefits of RIAA.  If you can justify adding another dependency, use boost::ptr_vector<Base>.
Iago
Monday, September 25, 2006
 
 
Hi,

In answer to some questions and some more clarification.

Omer: As mentioned, this isn't required but I tried it anyway.

Luc:  The call to the derived _processMsg is from the base  class ::processMessage.  This is probably part of the problem but still seems like the casting is causing it.  I am going to twiddle a few things this afternoon.

Micha: I didn't know about the '_' reserved for C++ implementation.  I have used that prefix to define private methods for a long time.  Force of habit at this point.  That would be a hard habit to break :)

I am questioning the design of this but it does feel correct.  The objects in question are all messages being streamed from a serial port.  Each message is identical except for its contents(how it is handled, parsed,etc).  In order to process these messages, I wanted to pick them off of the RS232 stream and process accordingly. To accomplish this, it seemed straightforward to create a ptr array of only the messages I wanted (5 or so) and throw the rest away(~30 different msgs).  Each call to the base ProcessMessage returns a boolean if the message was its own signature and subsequently handled.  If not handled, check the next message in the array.  Specific handling of that message would then drop to the derived implementation of the _processMsg from the base class ProcessMessage.
Help please....Oh please help :)
Monday, September 25, 2006
 
 
Man, I should be the poster child for knowing when to quit when tired.  This is a big egg on my face moment :) but thanks everyone for the awesome off hours support.

In case anyone is interested, the _processMsg function looked
like
Base:    virtual BOOL _processMsg();
Derived:  virtual BOOL _processMsg(char* cMsg);

Doh.  I forgot I had changed this around a little bit but didn't change the base class declare.

Really.  Thanks a lot for the help. 
And not even one flame. :))

-Dan
Help please....Oh please help :)
Monday, September 25, 2006
 
 
Dan,

Declare the function as pure virtual in the base class, like so:

virtual BOOL _processMsg() = 0;

The " = 0" means that the base class does not provide an implementation of the function, and so a derived class must provide an implementation of it. This would turn your mistake with differing signatures into a compile error.
Exception Guy Send private email
Tuesday, September 26, 2006
 
 
Thanks, Exception Guy. 

As it turns out that is exactly how I ended up finding it.
Help please....Oh please help :)
Wednesday, September 27, 2006
 
 
A decent rule of thumb (not always valid though) is that when you have classes with virtual functions, it's likely that their identity matters, so never allocate them on the stack. Forbid copy constructor and assignment operator to enforce this.
Wouter Lievens Send private email
Wednesday, October 18, 2006
 
 

This topic is archived. No further replies will be accepted.

Other recent topics Other recent topics
 
Powered by FogBugz