reference to pointer of derived class

M

Moes

struct Base{
};

struct Derived: public Base{
};

void f( Base *pBase )
{
Derived *pDerived = (Derived*)pBase;
}

Is there any way to make pDerived a reference to pBase?
 
M

Mike Wahler

Moes said:
struct Base{
};

struct Derived: public Base{
};

void f( Base *pBase )
{
Derived *pDerived = (Derived*)pBase;

I'll guess that you wrote that cast to silence
a compiler warning. What are you really trying
to do? (The above doesn't do what you probably
think). Research 'slicing'.
}

Is there any way to make pDerived a reference to pBase?

Syntactically, yes, but I think we should first determine
if that's really what you need. What are you trying to do?

-Mike
 
M

Moes

Mike Wahler said:
I'll guess that you wrote that cast to silence
a compiler warning. What are you really trying
to do? (The above doesn't do what you probably
think). Research 'slicing'.


Syntactically, yes, but I think we should first determine
if that's really what you need. What are you trying to do?

I'm trying make the cast to pDerived as efficient as possible, so I figured
a reference would be faster than a copy. Maybe I should mention that Base
is a pure virtual class, and f will cast pBase to one of a number of classes
derived from Base, depending on context.
 
T

Thomas Matthews

Moes said:
I'm trying make the cast to pDerived as efficient as possible, so I figured
a reference would be faster than a copy. Maybe I should mention that Base
is a pure virtual class, and f will cast pBase to one of a number of classes
derived from Base, depending on context.

Many compilers will just ignore casts, so that there is no
copy operation performed:
void g(Base * p_base)
{
Derived * p_derived = (Derived *) p_base;
/* This line _may_ not perform a copy operation.
The p_derived is pointing to the same object
as p_base. Only the _type_ is being changed.
*/
}
Often times, casting a pointer to base to a pointer to child
is only a semantic issue. The cast allows access to the
derived classes members and functions, which is more a compile
time issue than a run-time issue. Hopefully, changing permission
does not affect the value in the pointer.

See also: dynamic_cast
And read the FAQs below.

--
Thomas Matthews

C++ newsgroup welcome message:
http://www.slack.net/~shiva/welcome.txt
C++ Faq: http://www.parashift.com/c++-faq-lite
C Faq: http://www.eskimo.com/~scs/c-faq/top.html
alt.comp.lang.learn.c-c++ faq:
http://www.comeaucomputing.com/learn/faq/
Other sites:
http://www.josuttis.com -- C++ STL Library book
http://www.sgi.com/tech/stl -- Standard Template Library
 
M

Mike Wahler

Moes said:
I'm trying make the cast to pDerived as efficient as possible,

You misunderstand my question. Why do you feel you need to
convert a 'Base *' to a 'Derived *'? I was asking, 'what are
you trying to achieve', not 'describe how you're trying to achieve it'
a reference would be faster than a copy. Maybe I should mention that Base
is a pure virtual class, and f will cast pBase to one of a number of classes
derived from Base,

But why? The need for downcasting imo casts much suspicion upon
the design.

-Mike
 
M

Moes

Mike Wahler said:
You misunderstand my question. Why do you feel you need to
convert a 'Base *' to a 'Derived *'? I was asking, 'what are
you trying to achieve', not 'describe how you're trying to achieve it'

Is it that uncommon to have a function take a base class pointer, and treat
the object pointed to by that pointer as one of a number of possible derived
types (discovered via an enum "type" member of the base class which is
guaranteed to be set correctly depending on which derived type the object
is)?

struct Car{
enum Makes{ makeToyota, makeHonda };

virtual ~Car() = 0;
Car( Makes m ){ make = m; }
Makes make;
};

struct Toyota : public Car{
Toyota() : Car( makeToyota ){}
void ToyotaFunc(){}
};

struct Honda : public Car{
Honda() : Car( makeHonda ){}
void HondaFunc(){}
};

void f( Car *pCar )
{
switch( pCar->make )
{
case Car::makeToyota:
{
Toyota *pToyota = (Toyota*)pCar;
pToyota->ToyotaFunc();
break;
}
case Car::makeHonda:
{
Honda *pHonda = (Honda*)pCar;
pHonda->HondaFunc();
break;
}
}
}
 
J

Jerry Coffin

Moes wrote:

[ ... ]
Is it that uncommon to have a function take a base class pointer, and
treat the object pointed to by that pointer as one of a number of
possible derived types (discovered via an enum "type" member of the
base class which is guaranteed to be set correctly depending on which
derived type the object is)?

A "switch on type" is one of the classic signs of a design that
_probably_ has problems. One of the most basic ideas of virtual
functions is to allow you to avoid worrying about the specific type,
and instead treat all the objects the same way, with each one
implementing that function as needed.
struct Car{ [ ... ]
struct Toyota : public Car{ [...]
struct Honda : public Car{
[ ... ]

The question where would be "what sort of thing is there about a Honda
that's _fundamentally_ different from a Toyota?" The answer is usually
"not much" -- differences should normally be determined by function,
not something like brand.

There are times it makes sense to do something vaguely like this -- for
example, if you had:

struct car
struct SUV_4WD : public car

it might make sense for SUV_4WD to have a "lock_front_hubs" member
that's missing from a generic car, and you might have code like:

if (car.stuck()) {
SUV_4WD *p = dynamic_cast<SUV_4WD &>(car);
if (NULL != p) {
p.lock_front_hubs();
p.retry_moving();
}
}

OTOH, while there may be some excuse for this, it's still really
working at a relatively low level of abstraction, and we can do better
-- it would usually be better to have a virtual function
"maximize_traction". In the base class it would be a NOP, but in the
4WD class it would lock the hubs (and possibly shift the transmission
to low range as well).

To answer your question more directly, no it's not that uncommon -- but
it should be!
 
J

Jerry Coffin

Jerry Coffin wrote:
[ some screwed up code ... ]

[ ... ]
if (car.stuck()) {
SUV_4WD *p = dynamic_cast<SUV_4WD &>(car);
if (NULL != p) {
p.lock_front_hubs();
p.retry_moving();
}
}

I started to switch this from using a reference to a pointer, and than
accidentally posted it before I finished -- I apologize for that. I
think this should at least be a bit closer:

if ( car.stuck()) {
SUV_4WD *p = dynamic_cast<SUV_4WD *>(&car);

if (NULL != p) {
p->lock_front_hubs();
p->retry_moving();
}
}
 
M

Moes

Jerry Coffin said:
Jerry Coffin wrote:
[ some screwed up code ... ]

[ ... ]
if (car.stuck()) {
SUV_4WD *p = dynamic_cast<SUV_4WD &>(car);
if (NULL != p) {
p.lock_front_hubs();
p.retry_moving();
}
}

I started to switch this from using a reference to a pointer, and than
accidentally posted it before I finished -- I apologize for that. I
think this should at least be a bit closer:

if ( car.stuck()) {
SUV_4WD *p = dynamic_cast<SUV_4WD *>(&car);

if (NULL != p) {
p->lock_front_hubs();
p->retry_moving();
}
}

Thanks for the response. The code in question is actually in the operator=
of a class in which I have a pointer to a pure virtual base class (such as
"Car" in my example). I have to set that pointer to a new derived class
(either new TOyota() or new Honda() in the example) depending on what type
the source class's member points to. how would this be achieved without a
switch on type? (In the real class it's a vector of pointers, instad of a
single member pointer, in case that matters.)

struct Garage{

Garage(){ pCar = NULL; }

~Garage(){ if( pCar ) delete pCar; } // Will this call the correct
destructor for derived classes?

Garage& operator=( const Garage &source ){
if( pCar )
delete pCar; // may need the below switch here too so the correct
destructor is called?
switch( source.pCar->make )
{
case makeToyota:
pCar = new Toyota();
*((Toyota*)pCar) = *((Toyota*)source.pCar);
break;
case makeHonda:
pCar = new Honda();
*((Honda*)pCar) = *((Honda*)source.pCar);
break;
}
}

Car *pCar
};
 
I

Ian McCulloch

Moes said:
struct Base{
};

struct Derived: public Base{
};

void f( Base *pBase )
{
Derived *pDerived = (Derived*)pBase;
}

Is there any way to make pDerived a reference to pBase?

Old-style C casts are bad news. Use a C++ cast.

The only cast that is always possible here is

Derived *pDerived = dynamic_cast<Derived*>(pBase);

This assumes that Base and Derived are polymorphic types (ie. Base has at
least one virtual function). This will do a test to see if the pBase
pointer does in fact point to a Derived object; if it does not, it will
give a null pointer.

eg,

int main()
{
Base* pd = new Derived();
Base* pb = new Base();

Derived* d1 = dynamic_cast<Derived*>(pd); // fine. d1 == pd
Derived* d2 = dynamic_cast<Derived*>(pb); // no conversion. d2 == NULL
}

If you know for sure that the pointer you are given is definitely a pointer
to the derived type, you can replace the dynamic_cast with a static_cast.
But that is very dangerous, if you are wrong it is undefined behaviour, and
anything could happen.
 
B

Bogdan Sintoma

Hi
Thanks for the response. The code in question is actually in the operator=
of a class in which I have a pointer to a pure virtual base class (such as
"Car" in my example). I have to set that pointer to a new derived class
(either new TOyota() or new Honda() in the example) depending on what type
the source class's member points to. how would this be achieved without a
switch on type?
As Jerry already told you: virtual functions.
(In the real class it's a vector of pointers, instad of a
single member pointer, in case that matters.)
It doesn't matter.
struct Garage{
Garage(){ pCar = NULL; }
Garage() : pCar(0) {}
~Garage(){ if( pCar ) delete pCar; } // Will this call the correct
destructor for derived classes?
Yes, if the Car class has a virtual destructor.
And you don't need "if". If the pCar is NULL delete will do nothing. If
it is not NULL but already deleted you are in trouble anyway :).
Garage& operator=( const Garage &source ){
if( pCar )
delete pCar; // may need the below switch here too so the correct
destructor is called?
In your "real" code did you test for self-assignment?
You should implement the op. = in terms of the copy constructor and
swap.
Or, try this:
|Garage& operator=( const Garage &source )
|{
| if( this != &source)
| {
| delete pCar;
| pCar = 0; //NULL
| if( source.pCar )
| pCar = source.pCar->clone();
| }
| return *this;
|}
Where clone() is a virtual function declared in the base class and
implemented in each class derived from Car something like:
|virtual Car* clone() const
|{
| return new ThisCarSpecificClass( *this );
|}
Here "virtual" is optional, but I prefer to write it anyway.

And by the way, please see the faq:
http://www.parashift.com/c++-faq-lite/virtual-functions.html#faq-20.6
..
..
Best regards,
Bogdan Sintoma
 
M

Moes

In your "real" code did you test for self-assignment?
You should implement the op. = in terms of the copy constructor and
swap.

Is this a general rule of thumb, or specific to my code? How would one
implement operator= in terms of copy construcor in practice? (Sorry if this
is a FAQ, feel free to ignore if the question is irritating.)
Or, try this:
|Garage& operator=( const Garage &source )
|{
| if( this != &source)
| {
| delete pCar;
| pCar = 0; //NULL
| if( source.pCar )
| pCar = source.pCar->clone();
| }
| return *this;
|}
Where clone() is a virtual function declared in the base class and
implemented in each class derived from Car something like:
|virtual Car* clone() const
|{
| return new ThisCarSpecificClass( *this );
|}
Here "virtual" is optional, but I prefer to write it anyway.

Thank you. Your tip to check whether this == &source in the operator= saved
me some headaches.

Regarding doing something like clone(), I guess I have a natural inclination
against having a public member function that creates memory that has to be
destroyed by the calling code, but maybe that's not a big deal. Thanks
again for your response.
 
S

Shezan Baig

Moes said:
Regarding doing something like clone(), I guess I have a natural inclination
against having a public member function that creates memory that has to be
destroyed by the calling code, but maybe that's not a big deal. Thanks
again for your response.

Its not a big deal, your caller will have to take ownership of the
newly created object anyway. Otherwise who will?

-shez-
 
B

Bogdan Sintoma

Moes said:
Is this a general rule of thumb, or specific to my code? How would one
implement operator= in terms of copy construcor in practice? (Sorry if this
is a FAQ, feel free to ignore if the question is irritating.)

You can find the answer (and not only at this question) here:
http://www.gotw.ca/gotw/059.htm
Regarding doing something like clone(), I guess I have a natural inclination
against having a public member function that creates memory that has to be
destroyed by the calling code, but maybe that's not a big deal.

Well, actually it is, especially in the presence of exceptions. Here
are some links:
http://www.research.att.com/~bs/bs_faq2.html#exceptions
http://www.boost.org/libs/smart_ptr/shared_ptr.htm

And further, if you "google" for RAII, smart pointers and exceptions,
you will find a lot of articles.

....
Bogdan
 

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
474,270
Messages
2,571,353
Members
48,041
Latest member
Oliwen826

Latest Threads

Top