By accident I found that with a polymorphic type using dynamic_cast back to the
ID: 658528 • Letter: B
Question
By accident I found that with a polymorphic type using dynamic_cast back to the derived class will throw a 'bad cast' error if the pointer is no longer valid. Is this undefined behavior or could this be a way to check for valid pointers without using smart pointers?
I'm not really against using smart pointers as they serve a real need, but I dislike the syntax. I don't have anything against templated types but I find smart pointers to bloat the code and I'm not a fan of macro's to try and avoid that bloat. I wish C++ included something in the language syntax itself vs using the template feature but with this question I'm more concerned about what is happening below and if this is a valid and defined way to checking for valid pointers as this gives an exception every time from my tests.
#include <string>
#include <map>
using namespace std;
class Base
{
public:
virtual ~Base(){}
};
class Derived : public Base
{
public:
virtual ~Derived(){}
};
class Storage
{
private:
map<string, Base*> storage;
public:
void AddItem(string name, Base* base)
{
if (storage.find(name) == storage.end())
{
storage[name] = base;
}
}
template <class T>
T& FindItem(string name)
{
if (storage.find(name) != storage.end())
{
Base* item = storage[name];
return dynamic_cast<T&>(*item);
}
throw;
}
};
int main()
{
Storage store;
// force 'd' to go out of scope for our test
{
Derived d;
store.AddItem("test", &d);
}
// this will throw a bad cast exception
Derived& test = store.FindItem<Derived>("test");
return 0;
}
Explanation / Answer
dynamic-cast needs to deference the pointer to find the proper offset it should be at, dereferencing an invalid pointer is undefined behavior,
one thing you might see:
class Derived2 : public Base
{
public:
virtual ~Derived2(){}
};
int main()
{
Storage store;
{
Derived d;
store.AddItem("test", &d);
}
{
Derived2 d2;
Derived2& test = store.FindItem<Derived2>("test");//might not throw an exception
//test might refer to d2
}
return 0;
}
Messing about with a void* buffer and placement new and destroy will let me insert anything in there.
Just go with smart pointers and learn to appreciate them for the blessing that they are.