PDA

View Full Version : Is the down casting right, and why dynamic_cast fails?



QtCrawler
8th December 2015, 10:35
Hello guys,
here I have a snippet of code and it would be very helpful, if you can explain why the second dynamic_cast fails and if the static_cast is the way to do it.

There are three classes Base, MyString and MyList. MyString and MyList are derived from Base. MyList is a template-class

#include <QCoreApplication>
#include <QDebug>

class Base
{
public:
Base(){

}

virtual ~Base(){
qDebug() << "~Base()";
}
};

class MyString : public Base
{
public:
QString str;

public:
MyString(const QString& str) : Base(), str(str){

}

virtual ~MyString(){
qDebug() << "~MyString()";
}
};

template <class T>
class MyList : public QList<T>, public Base
{
public:
MyList<T>():QList<T>(){

}

MyList<T>(QList<T> h):QList<T>(h){

}

virtual ~MyList<T>(){
qDebug() << "~MyList()";
}
};

Base* createMyList()
{
MyList<Base*>* myList = new MyList<Base*>();
myList->append(new MyString("joey potter"));
myList->append(new MyString("allen harper"));
myList->append(new MyString("charlie harper"));
myList->append(new MyString("john mcclane"));

return myList;
}

int main(int argc, char *argv[])
{
QCoreApplication a(argc, argv);
Q_UNUSED(a);

MyList<Base*>* myList = new MyList<Base*>();
myList->append(new MyString("string1"));

//I know that first element is from class MyString so static_cast is sufficient
Q_ASSERT(dynamic_cast<MyString*>(myList->first()) != NULL);// ok
Q_ASSERT(static_cast<MyString*>(myList->first()) != NULL);// ok

MyList<MyString*>* anotherList = static_cast<MyList<MyString*>*>(createMyList());
Q_ASSERT(anotherList != NULL);// ok

MyList<MyString*>* differentList = dynamic_cast<MyList<MyString*>*>(createMyList());
Q_ASSERT(differentList != NULL);// ASSERT: "differentList != NULL" in file ../dynamic_cast_test/main.cpp, line 74







//free ram
QListIterator<Base*> lit1(*myList);
while(lit1.hasNext()){
delete lit1.next();
}
delete myList;

QListIterator<MyString*> lit2(*anotherList);
while(lit2.hasNext()){
delete lit2.next();
}
delete anotherList;

QListIterator<MyString*> lit3(*differentList);
while(lit3.hasNext()){
delete lit3.next();
}
delete differentList;

return 0;
}

Why does following cast fail?

MyList<MyString*>* differentList = dynamic_cast<MyList<MyString*>*>(createMyList())
Cause MyList<Base*>* is completely different from MyList<MyString*>* and the dynamic check is technically impossible ?

But can I do a static_cast(no dynamic checks), cause I know it's a MyList<MyString*>*? Is this cast is safe?

thank you

anda_skoa
8th December 2015, 11:16
If createMyList() returns Base* but actually returns a pointer to an object of MyList<MyString*>, then the dynamic cast should work.

If createMyList() returns MyList<Base*>* then this is not a base type of MyList<MyString*>, so the dynamic cast will return 0 and the static_cast will likely also not work (since the target is not a subclass of the source).

Cheers,
_

QtCrawler
8th December 2015, 14:02
thank you. but why the static_cast works properly in my snippet? Does it work accidentally?
And what's the best way to cast, if it's like in my code. you have a pointer Base* which is actually a pointer of MyList<Base*>* and you want to cast to MyList<MyString*>*. Do we have to do it in two steps?

QtCrawler
9th December 2015, 22:21
And what's the best way to cast, if it's like in my code. you have a pointer Base* which is actually a pointer of MyList<Base*>* and you want to cast to MyList<MyString*>*. Do we have to do it in two steps?
Does anybody know an answer to the question above? :)

d_stranz
10th December 2015, 19:47
Your code is more than a little bit confusing. Why do you use Base as the base class for both the list -and- the things in the list? C++ inheritance is intended to express "is-a" relationships: a MyString is-a Base. But your code also claims that MyList< Base * > also is-a Base. You're trying to use this empty, bogus Base class so you can cast things to each other that can't really be cast.

A MyList< Base * > is a different class than a MyList< MyString * >, even if the contents of each list can be up or down-cast to each other.

A static cast works because a static cast says, "I don't care if the thing I am trying to cast is completely unrelated to the thing I'm trying to cast it to, just do it", so of course it works for your case. Dynamic casting doesn't work because the two list types are unrelated even if their contents are. At best, you might be able to dynamically cast the MyList< Base * >* to a Base *, and then dynamically cast that to a MyList< MyString * >*, but then your code says you could also cast it to a MyString * (which of course it isn't) by virtue of the confused inheritance tree you've defined.

QtCrawler
11th December 2015, 12:49
Thank you for your explanation. But what's the reason to use two dynamic casts instead of two static casts(or maybe one static cast). I thought I need dynamic casts just in cases, where I'm not sure if the pointer holds the to-casting-pointer or not at runtime.

In serialisaton (i.e xml-communication between client and server)implementations I found such an inheritance design. A list should be serializeable and a string too, so they are derived classes from one base class.

d_stranz
13th December 2015, 01:18
In serialisation (i.e xml-communication between client and server)implementations I found such an inheritance design. A list should be serializeable and a string too, so they are derived classes from one base class.

That's a quite different use of the concept. Just like in Qt, all classes that can have properties, signals, slots, and so forth inherit from QObject, in the serialization example, they undoubtedly inherit from a base class that defines the methods required by a serializable object. The base class isn't there to provide a workaround for anything, it defines an interface which all serializable objects must implement, and establishes a true "is-a" relationship for derived classes.

Your "Base" class doesn't define anything, doesn't represent anything other than an empty name. You aren't using it to define the behavior and semantics of a class hierarchy, you're trying to force an unnatural relationship between classes that aren't related. This isn't how inheritance in C++ is intended to be used.

Static casting does no type-checking, no verification that the first type can actually be cast to the second type. It's a brute force way to do something that is generally unsafe and often incorrect. If dynamic casting won't work, it means you are trying to do something that shouldn't be done, and so you shouldn't be using a static cast to force the compiler to accept it.

QtCrawler
14th December 2015, 08:54
Thank you very much for your help.

That's a quite different use of the concept. Just like in Qt, all classes that can have properties, signals, slots, and so forth inherit from QObject, in the serialization example, they undoubtedly inherit from a base class that defines the methods required by a serializable object. The base class isn't there to provide a workaround for anything, it defines an interface which all serializable objects must implement, and establishes a true "is-a" relationship for derived classes.

Your "Base" class doesn't define anything, doesn't represent anything other than an empty name. You aren't using it to define the behavior and semantics of a class hierarchy, you're trying to force an unnatural relationship between classes that aren't related. This isn't how inheritance in C++ is intended to be used.

I totally agree with you. I provided that example not, cause it's the common use and the sense of inheritence in c++. I provided it, to point out the casting issue I've been trying to understand.

Added after 54 minutes:

One last question: If there aren't two cast steps necessary. I. e.


MyString* myString = dynamic_cast<MyString*>(baseObject);


Is it ok to use static_cast?


At best, you might be able to dynamically cast the MyList< Base * >* to a Base *, and then dynamically cast that to a MyList< MyString * >*, but then your code says you could also cast it to a MyString * (which of course it isn't) by virtue of the confused inheritance tree you've defined.
I tried your advice, but without success:

MyList<MyString*>* differentList2 = dynamic_cast<MyList<MyString*>*>(dynamic_cast<Base*>(createMyList()));
//differentList2 is a Null-Pointer

Do you know the reason?

d_stranz
15th December 2015, 04:46
Do you know the reason?

You need to break the compound dynamic cast statement into two statements so you can see which of the two casts is the one that fails. I suspect that you aren't going to be able to outwit the compiler on this one, because it knows as well as you do that MyList< MyString * > and MyList< Base *> are different classes that cannot be interconverted even if the contents can be.

Why don't you take a step back and figure out why simply using MyList< Base * > everywhere isn't adequate? If all of the things you want to put in lists inherit from Base, then you can always construct lists containing any of the inherited class pointers. You can even mix list content pointers if you want. You pass MyList< Base * > pointers everywhere, and do your dynamic casting on the pointers in the list, not the list itself.

I don't know what you intend to do with your list contents, but if you are clever with the interface you design for Base, you may not have to do any type casting at all. A virtual function in Base, reimplemented in each derived class, could be called as you iterate over the list. Because it is virtual, the compiler will cause the appropriate derived class method to be executed. So a virtual Base:: doSomething() method executed on a list of MyString pointers actually calls MyString:: doSomething().

QtCrawler
15th December 2015, 08:26
You need to break the compound dynamic cast statement into two statements so you can see which of the two casts is the one that fails. I suspect that you aren't going to be able to outwit the compiler on this one, because it knows as well as you do that MyList< MyString * > and MyList< Base *> are different classes that cannot be interconverted even if the contents can be.
The second(outer) cast fails. Ok, so I bark up the wrong tree :)


Why don't you take a step back and figure out why simply using MyList< Base * > everywhere isn't adequate? If all of the things you want to put in lists inherit from Base, then you can always construct lists containing any of the inherited class pointers. You can even mix list content pointers if you want. You pass MyList< Base * > pointers everywhere, and do your dynamic casting on the pointers in the list, not the list itself.

I don't know what you intend to do with your list contents, but if you are clever with the interface you design for Base, you may not have to do any type casting at all. A virtual function in Base, reimplemented in each derived class, could be called as you iterate over the list. Because it is virtual, the compiler will cause the appropriate derived class method to be executed. So a virtual Base:: doSomething() method executed on a list of MyString pointers actually calls MyString:: doSomething().
Yes I thought about it, but I like the existing solution. I know you think it's a bit confusing, and not the best style, but I think it's very flexibly and I have to do the casting just in the methods of the interface-class. So all other classes are able to call the remote-procedure-calls via the interface-instance and get what they expect. The only last element of uncertainty is the casting-issue. I saw implementations which use c-style casts[(MyList<MyString*>) createMyList()], which worked for whatever reason, but using c-style casting in a c++ app is like using a prybar. So I thought about static_cast, which worked (You explained why and I read the docs about casting). Maybe I should use static_cast finally. If I use it in the interface-class, I'm hundred percent sure, that the casting pointer holds the expected pointer, so it should be safe enough, shouldn't it?

By all means, I'm thankful for your help and input.

d_stranz
15th December 2015, 17:01
If I use it in the interface-class, I'm hundred percent sure, that the casting pointer holds the expected pointer, so it should be safe enough, shouldn't it?

If you are the only one who will ever use your code, and if you are always sure that what you are using a static_cast for is what you think it should be, it will be safe.

The problem with static_cast in code that others might use is that they may not realize what is happening "under the hood" and might pass the wrong thing into a function. Because static_cast is a prybar (more like a sledgehammer) it will always work, but could lead to crashes and data corruption which are hard to diagnose. You step into a method, and none of the values in the data structure make any sense because the static_cast was done on the wrong data type.

It is best avoided in favor of a class structure that requires no casting of any sort.