started moving some libraries from msvc to mingw, and found really interesting behavior of msvc when one wants to delete an array of upcasted objects. Namely msvc does some dark magic (it seems to love doing that) and the bellow code executes just fine, however in mingw (4.7.2( crashes. I believe that mingw is performing correctly and its the msvc voodoo thats the issue for making a sleeper bug.
Code:
#include <iostream>
class foo{
static int idgen;
protected:
int id;
public:
foo(){
id = idgen++;
std::cout << "Hello ( foo - "<<id<<")"<<std::endl;
}
virtual ~foo(){
std::cout << "Bye bye ( foo - "<<id<<")"<<std::endl;
};
};
int foo::idgen = 0;
class bar: public foo{
double some_data[20];
public:
bar(){
std::cout << "Hello ( bar - "<<id<<")"<<std::endl;
}
~bar(){
std::cout << "Bye bye ( bar - "<<id<<")"<<std::endl;
}
};
int main()
{
const unsigned int size = 2;
foo** arr = new foo*[size];
{
bar* tmp = new bar[size];
for(int i=0; i<size; i++)
{
arr[i] = &(tmp[i]); //take address of each object
}
}
delete [] arr[0]; //take address of first object, pointer is same as tmp. This also crashes mingw
delete [] arr;
}
Output from msvc 2010
Hello ( foo - 0)
Hello ( bar - 0)
Hello ( foo - 1)
Hello ( bar - 1)
Bye bye ( bar - 1)
Bye bye ( foo - 1)
Bye bye ( bar - 0)
Bye bye ( foo - 0)
And mingw (crashed at the destruction)
Hello ( foo - 0)
Hello ( bar - 0)
Hello ( foo - 1)
Hello ( bar - 1)
My question is, what is the correct approach to fix this. The current hackfix that I came up with involved just trying to downcast to every possible class and invoking the delete operation on the downcasted pointer:
if(dynamic_cast<bar*>(arr[0]) != 0)
delete [] dynamic_cast<bar*>(arr[0]);
Is there a better approach, besides redesigning the library (it isn't mine)?
In the standard specifications, Section 5.3.5 paragraph 3, regarding the delete
operator:
[...] In the second alternative (delete array) if the dynamic type of the object to be deleted differs from its static type, the behavior is undefined.
So indeed, you should not rely on the gentle behaviour of Visual C++ in this case, and try to provide the array delete
operator a pointer of the correct type, which basically means dynamic casting in your situation.
You may avoid that problem by using a vector instance to store your upcasted objects allocated one by one.