469,299 Members | 2,050 Online
Bytes | Developer Community
New Post

Home Posts Topics Members FAQ

Post your question to a community of 469,299 developers. It's quick & easy.

Problem with static downcast of base type to derived type

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
}
}
}
==============================================
Jul 2 '08 #1
10 3247
On 2008-07-02 21:13, Dom Jackson wrote:
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
Jul 2 '08 #2
On Jul 2, 4:31 pm, Erik Wikström <Erik-wikst...@telia.comwrote:
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
Jul 2 '08 #3
Comments inline:
Dom Jackson <no****@mci2000.comwrote in
news:6q********************************@4ax.com:
I have a program which crashes when:
[snip.. the code is more interesting]
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.
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...
dptr->dfunc(); // crashes - see above
.... particularly when you attempt to access members which only exist in
D objects.
}
}
}
==============================================

Jul 2 '08 #4
On Jul 2, 1:31*pm, Erik Wikström <Erik-wikst...@telia.comwrote:
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.
Of course the OP must have at least one virtual function in Base for
dynamic_cast to work.
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.
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
Jul 2 '08 #5
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
Jul 2 '08 #6
On Jul 2, 10:48 pm, Jonathan Mcdougall <jonathanmcdoug...@gmail.com>
wrote:
On Jul 2, 4:31 pm, Erik Wikström <Erik-wikst...@telia.comwrote:
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.
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.
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:ja*********@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
Jul 3 '08 #7
On Jul 2, 11:06 pm, acehr...@gmail.com wrote:
On Jul 2, 1:31 pm, Erik Wikström <Erik-wikst...@telia.comwrote:
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.
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.
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.
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.
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.
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.)
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:ja*********@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
Jul 3 '08 #8
On Jul 2, 11:29 pm, Dom Jackson <nos...@mci2000.comwrote:
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.
Can anyone tell me what the problem is here?
There is no problem. In C++, containers contain objects, with
value semantics.
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:ja*********@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
Jul 3 '08 #9
On Jul 2, 1:31*pm, Erik Wikström <Erik-wikst...@telia.comwrote:
On 2008-07-02 21:13, Dom Jackson wrote:
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());).

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
Jul 3 '08 #10
Thanks everyone - I re-implemented my set as a collection of pointers
and it's all working now.

Dom
Jul 3 '08 #11

This discussion thread is closed

Replies have been disabled for this discussion.

Similar topics

3 posts views Thread by Omer van Kloeten | last post: by
12 posts views Thread by cppaddict | last post: by
4 posts views Thread by Bangalore | last post: by
8 posts views Thread by | last post: by
1 post views Thread by CARIGAR | last post: by
1 post views Thread by Geralt96 | last post: by
reply views Thread by harlem98 | last post: by
By using this site, you agree to our Privacy Policy and Terms of Use.