Problem with static downcast of base type to derived type
Question posted by: Dom Jackson
(Guest)
on
July 2nd, 2008 07:15 PM
I have a program which crashes when:
1 - I use static_cast to turn a base type pointer into a pointer to a
derived type
2 - I use this new pointer to call a function in an object of the
derived type
3 - this function then 'grows' the derived type object (by pushing
onto a vector).
Is this actually possible, or should I be doing something else? In
more detail, what I'm doing is (there's a complete test program below,
which crashes):
1 - I have a base class which doesn't do very much;
2 - I declare a derived class with public inheritance from the base
class;
3 - the derived class has a method which pushes onto a vector in the
derived class;
4 - I create an STL set of base class objects;
5 - I then insert an object of the derived class into this set. I use
the set 'insert' method, which returns an iterator to the new object
in the set, but this is an iterator to a *base* object (I hope)
6 - I convert the iterator to a pointer, and static_cast the pointer
to a pointer to an object of the derived class
7 - I call my new method via this pointer, pushing onto the vector in
the derived-class-object. Bang.
Any help much appreciated.
Thanks -
Dom
===========================================
#include <vector>
#include <set>
#include <iostream>
using std::vector;
using std::set;
using std::cout;
class A {
public:
A(int v) :
val(v) {}
int val;
};
class D : public A {
public:
D(int v) :
A(v) {}
mutable vector<intvec;
void dfunc(void) const {
std::cout << "hello world " << val << "!\n";
// ********************************************
// Ok without this line, crashes with this line
vec.push_back(val);
// ********************************************
}
};
class ALess {
public :
bool operator() (const A& a1, const A& a2) const {
return a1.val < a2.val;
}
};
typedef set<A, ALess ASet;
typedef ASet::iterator ASetIter;
typedef std::pair<ASetIter, bool ASetInsRetVal;
int main() {
ASetInsRetVal retval;
ASet aset;
for(int i=0; i<4; i++) {
retval = aset.insert(D(i));
if(!retval.second)
cout << "insert failed\n";
else {
const D *dptr = static_cast<const D*>(&(*retval.first));
dptr->dfunc(); // crashes - see above
}
}
}
==============================================
Would you like to answer this question?
Sign up for a free account, or Login (if you're already a member).
|
|
July 2nd, 2008 08:35 PM
# 2
|
Re: Problem with static downcast of base type to derived type
On 2008-07-02 21:13, Dom Jackson wrote:
Quote:
Originally Posted by
I have a program which crashes when:
>
1 - I use static_cast to turn a base type pointer into a pointer to a
derived type
>
2 - I use this new pointer to call a function in an object of the
derived type
>
3 - this function then 'grows' the derived type object (by pushing
onto a vector).
>
Is this actually possible, or should I be doing something else? In
more detail, what I'm doing is (there's a complete test program below,
which crashes):
>
1 - I have a base class which doesn't do very much;
>
2 - I declare a derived class with public inheritance from the base
class;
>
3 - the derived class has a method which pushes onto a vector in the
derived class;
>
4 - I create an STL set of base class objects;
>
5 - I then insert an object of the derived class into this set. I use
the set 'insert' method, which returns an iterator to the new object
in the set, but this is an iterator to a *base* object (I hope)
>
6 - I convert the iterator to a pointer, and static_cast the pointer
to a pointer to an object of the derived class
>
7 - I call my new method via this pointer, pushing onto the vector in
the derived-class-object. Bang.
|
This will not work, std::set holds objects of the type given as a
parameter, in this case base-objects and not derived objects, the
derived objects will be sliced into base objects. So what you are doing
is trying to call a function which does not exist to manipulate a vector
that does not exist.
To solve your problem you could use a set of pointers to base instead of
a set of base (e.g. std::set<Base*instead of std::set<Base>) and
create the objects using new (e.g. set.insert(new Derived());).
Besides that, when casting from base to derived you should use
dynamic_cast and check if the cast succeeded (in which case the returned
pointer will be non-0). If you do that in your code you will notice that
you will always get a 0-pointer. A cast from pointer to base class to a
pointer to derived is only allowed if the object pointed to is of type
derived.
You can always cast from pointer to derived to pointer to base using
static_cast, but when going the other way you shall use dynamic_cast.
--
Erik Wikström
|
|
July 2nd, 2008 08:55 PM
# 3
|
Re: Problem with static downcast of base type to derived type
On Jul 2, 4:31 pm, Erik Wikström <Erik-wikst...@telia.comwrote:
Quote:
Originally Posted by
You can always cast from pointer to derived to pointer to base using
static_cast, but when going the other way you shall use dynamic_cast.
|
"Shall" might be a bit strong here. There are cases where, because of
the
context, downcasting cannot fail. In this case, using dynamic cast
gives you
nothing except less performance.
You could however have a separate function that uses dynamic cast in
debug
but reverts to static cast in release, except for cases where the
dynamic cast
is actually part of the design (such as type switches, but these
should be
avoided when possible anyways).
--
Jonathan Mcdougall
|
|
July 2nd, 2008 09:05 PM
# 4
|
Re: Problem with static downcast of base type to derived type
Comments inline:
Dom Jackson <nospam@mci2000.comwrote in
news:6qjn645kup1jke5279liv592rced2lkn4j@4ax.com:
Quote:
Originally Posted by
I have a program which crashes when:
>
|
[snip.. the code is more interesting]
Quote:
Originally Posted by
Any help much appreciated.
>
Thanks -
>
Dom
>
===========================================
#include <vector>
#include <set>
#include <iostream>
>
using std::vector;
using std::set;
using std::cout;
>
class A {
public:
A(int v) :
val(v) {}
int val;
};
>
class D : public A {
public:
D(int v) :
A(v) {}
>
mutable vector<intvec;
>
void dfunc(void) const {
std::cout << "hello world " << val << "!\n";
// ********************************************
// Ok without this line, crashes with this line
vec.push_back(val);
// ********************************************
}
};
>
class ALess {
public :
bool operator() (const A& a1, const A& a2) const {
return a1.val < a2.val;
}
};
>
typedef set<A, ALess ASet;
typedef ASet::iterator ASetIter;
typedef std::pair<ASetIter, bool ASetInsRetVal;
>
int main() {
ASetInsRetVal retval;
ASet aset;
>
for(int i=0; i<4; i++) {
retval = aset.insert(D(i));
|
This will slice your temporary D object into an A object. ASet contains
objects of type A, and only type A.
Quote:
Originally Posted by
if(!retval.second)
cout << "insert failed\n";
else {
const D *dptr = static_cast<const D*>(&(*retval.first));
|
Undefined behaviour. retval->first is an A object. Which you then
force the compiler to try to treat it like a D object...
Quote:
Originally Posted by
dptr->dfunc(); // crashes - see above
|
.... particularly when you attempt to access members which only exist in
D objects.
Quote:
Originally Posted by
}
}
}
==============================================
>
|
|
|
July 2nd, 2008 09:15 PM
# 5
|
Re: Problem with static downcast of base type to derived type
On Jul 2, 1:31*pm, Erik Wikström <Erik-wikst...@telia.comwrote:
Quote:
Originally Posted by
On 2008-07-02 21:13, Dom Jackson wrote:
|
Quote:
Originally Posted by
Besides that, when casting from base to derived you should use
dynamic_cast and check if the cast succeeded (in which case the returned
pointer will be non-0). If you do that in your code you will notice that
you will always get a 0-pointer.
|
Of course the OP must have at least one virtual function in Base for
dynamic_cast to work.
Quote:
Originally Posted by
A cast from pointer to base class to a
pointer to derived is only allowed if the object pointed to is of type
derived.
|
True.
Quote:
Originally Posted by
You can always cast from pointer to derived to pointer to base using
static_cast, but when going the other way you shall use dynamic_cast.
|
static_cast is fine even for downcasting, as long as the target type
is correct. static_cast is for when the destination type is known at
compile time.
Did you mean that static_cast can not be used in polymorphic
hierarchies? If so, it's news to me and I don't have the standard
handy to check. :)
Ali
|
|
July 2nd, 2008 09:35 PM
# 6
|
Re: Problem with static downcast of base type to derived type
Thanks folks - I had no idea that you couldn't put a derived type in a
set of base types. I can't immediately see anything in Josuttis about
this. Can anyone tell me what the problem is here? Is this common to
all STL containers? I had rather naively assumed that an object of a
derived type actually was, to all intents and purposes, also a base
object, but that's obviously not the case.
Thanks -
Dom
|
|
July 3rd, 2008 07:35 AM
# 7
|
Re: Problem with static downcast of base type to derived type
On Jul 2, 10:48 pm, Jonathan Mcdougall <jonathanmcdoug...@gmail.com>
wrote:
Quote:
Originally Posted by
On Jul 2, 4:31 pm, Erik Wikström <Erik-wikst...@telia.comwrote:
|
Quote:
Originally Posted by
Quote:
Originally Posted by
You can always cast from pointer to derived to pointer to
base using static_cast, but when going the other way you
shall use dynamic_cast.
|
|
Quote:
Originally Posted by
"Shall" might be a bit strong here. There are cases where,
because of the context, downcasting cannot fail. In this case,
using dynamic cast gives you nothing except less performance.
|
It also gives you defined behavior in case you are mistaken.
You should use dynamic_cast until the profiler says otherwise,
even if you think you know that static_cast would be safe.
Quote:
Originally Posted by
You could however have a separate function that uses dynamic
cast in debug but reverts to static cast in release,
|
You don't really mean to say that you deliver different code
than what you've tested, do you? Again, unless the profiler
says it's absolutely imperative, you deliver the same code,
compiled with the same options, as you use during development.
--
James Kanze (GABI Software) email:james.kanze@gmail.com
Conseils en informatique orientée objet/
Beratung in objektorientierter Datenverarbeitung
9 place Sémard, 78210 St.-Cyr-l'École, France, +33 (0)1 30 23 00 34
|
|
July 3rd, 2008 07:35 AM
# 8
|
Re: Problem with static downcast of base type to derived type
On Jul 2, 11:06 pm, acehr...@gmail.com wrote:
Quote:
Originally Posted by
On Jul 2, 1:31 pm, Erik Wikström <Erik-wikst...@telia.comwrote:
|
Quote:
Originally Posted by
Quote:
Originally Posted by
On 2008-07-02 21:13, Dom Jackson wrote:
Besides that, when casting from base to derived you should
use dynamic_cast and check if the cast succeeded (in which
case the returned pointer will be non-0). If you do that in
your code you will notice that you will always get a
0-pointer.
|
|
Quote:
Originally Posted by
Of course the OP must have at least one virtual function in
Base for dynamic_cast to work.
|
Of course, if Base doesn't have at least one virtual function,
there's probably no point in deriving from it.
Quote:
Originally Posted by
Quote:
Originally Posted by
A cast from pointer to base class to a pointer to derived is
only allowed if the object pointed to is of type derived.
|
|
Quote:
Originally Posted by
True.
|
Quote:
Originally Posted by
Quote:
Originally Posted by
You can always cast from pointer to derived to pointer to
base using static_cast, but when going the other way you
shall use dynamic_cast.
|
|
Quote:
Originally Posted by
static_cast is fine even for downcasting, as long as the
target type is correct.
|
Which, of course, means that there is a distinct risk of an
error, resulting in undefined behavior. The choice is basically
whether you want undefined behavior or defined behavior in case
of an error.
Quote:
Originally Posted by
static_cast is for when the destination type is known at
compile time.
|
Static_cast is for many things, but in the case of Base* to
Derived*, it should only be used when the profiler says you have
no other choice. (Practically, of course, there should be very,
very few Base* to Derived* conversions to begin with.)
Quote:
Originally Posted by
Did you mean that static_cast can not be used in polymorphic
hierarchies? If so, it's news to me and I don't have the
standard handy to check. :)
|
It can only be used in limited ways: you can't convert from a
virtual base to a derived (pointer or reference), for example,
and you can't arbitrarily navigate in the hierarchy.
--
James Kanze (GABI Software) email:james.kanze@gmail.com
Conseils en informatique orientée objet/
Beratung in objektorientierter Datenverarbeitung
9 place Sémard, 78210 St.-Cyr-l'École, France, +33 (0)1 30 23 00 34
|
|
July 3rd, 2008 07:45 AM
# 9
|
Re: Problem with static downcast of base type to derived type
On Jul 2, 11:29 pm, Dom Jackson <nos...@mci2000.comwrote:
Quote:
Originally Posted by
Thanks folks - I had no idea that you couldn't put a derived
type in a set of base types. I can't immediately see anything
in Josuttis about this.
|
What should he say? He certainly says that the containers
contain objects of type T. In C++, an object of type T is an
object of type T, and not an object of some other type.
Polymorphism only works through references and pointers.
Quote:
Originally Posted by
Can anyone tell me what the problem is here?
|
There is no problem. In C++, containers contain objects, with
value semantics.
Quote:
Originally Posted by
Is this common to all STL containers? I had rather naively
assumed that an object of a derived type actually was, to all
intents and purposes, also a base object, but that's obviously
not the case.
|
It's never the case. An object of a derived type is an object
of a derived type, and an object of a base type is an object of
a base type. Every object has one, and only one type. This is
a fundamental aspect of the C++ object model (and also the
object model of many other languages).
Written correctly, an object of a derived type can be used as an
object of a base type, but only if the "use" involves an
indirection (reference or pointer). The object still has a
specific type, which never changes (but a pointer or a
reference can designate different objects).
--
James Kanze (GABI Software) email:james.kanze@gmail.com
Conseils en informatique orientée objet/
Beratung in objektorientierter Datenverarbeitung
9 place Sémard, 78210 St.-Cyr-l'École, France, +33 (0)1 30 23 00 34
|
|
July 3rd, 2008 08:35 PM
# 10
|
Re: Problem with static downcast of base type to derived type
On Jul 2, 1:31*pm, Erik Wikström <Erik-wikst...@telia.comwrote:
Quote:
Originally Posted by
On 2008-07-02 21:13, Dom Jackson wrote:
>
Quote:
Originally Posted by
4 - I create an STL set of base class objects;
|
>
Quote:
Originally Posted by
5 - I then insert an object of the derived class into this set. I use
the set 'insert' method, which returns an iterator to the new object
in the set, but this is an iterator to a *base* object (I hope)
|
>
Quote:
Originally Posted by
6 - I convert the iterator to a pointer, and static_cast the pointer
to a pointer to an object of the derived class
|
>
Quote:
Originally Posted by
7 - I call my new method via this pointer, pushing onto the vector in
the derived-class-object. Bang.
|
>
This will not work, std::set holds objects of the type given as a
parameter, in this case base-objects and not derived objects, the
derived objects will be sliced into base objects. So what you are doing
is trying to call a function which does not exist to manipulate a vector
that does not exist.
>
To solve your problem you could use a set of pointers to base instead of
a set of base (e.g. std::set<Base*instead of std::set<Base>) and
create the objects using new (e.g. set.insert(new Derived());).
>
You can always cast from pointer to derived to pointer to base using
static_cast, but when going the other way you shall use dynamic_cast.
|
No, a dynamic_cast<cannot be used in the original program to cast
from an A to a B pointer - because the A class is not a polymorphic
type.
Greg
|
|
July 3rd, 2008 09:45 PM
# 11
|
Re: Problem with static downcast of base type to derived type
Thanks everyone - I re-implemented my set as a collection of pointers
and it's all working now.
Dom
Not the answer you were looking for? Post your question . . .
182,372 Experts ready to help you find a solution.
Sign up for a free account, or Login (if you're already a member).
|
|
|
Top Community Contributors
|