Pointer-to-Pointer crashes in case where Pointer doesn't
I'm porting some code to Windows and am stumped. There is some code that runs automatically on launch to copy a pointer to a pointer, and runs again on exit to delete the pointer to a pointer if it is not null.
I have created a sample program to reproduce the behavior
int main()
{
// Pointer to a Pointer, current crash.
InterfaceClass** ptrptr;
ConcreteTwo* object = new ConcreteTwo();
ptrptr = (InterfaceClass**)(&object); // cast is required for some reason.
delete *ptrptr; // Crash here.
// Single pointer, works fine.
InterfaceClass* ptrptr;
ConcreteTwo* object = new ConcreteTwo();
ptrptr = object;
delete ptrptr;
// There are other cases where there are only 3 classes in the hierarchy.
// This also works fine.
InterfaceClass** ptrptr;
ConcreteOne* object = new ConcreteOne();
ptrptr = (InterfaceClass**)(&object);
delete *ptrptr;
return 0;
}
The Class Hierarchy looks like this. The base class is an interface with some pure virtual functions and is included by many classes all over the program in such a way that many objects potentially inherit it from more than one place. Because of this the concrete implementation must extend it with "publ开发者_JS百科ic virtual InterfaceClass". In this example deleting the "virtual" resolves the crash.
class InterfaceClass {
public:
virtual ~InterfaceClass() {};
InterfaceClass() {}
};
class ConcreteClass : public virtual InterfaceClass {
public:
ConcreteClass() { }
virtual ~ConcreteClass() {}
};
class ConcreteOne : public ConcreteClass
{
public:
ConcreteOne(void) {}
virtual ~ConcreteOne(void) {}
};
class ConcreteTwo : public ConcreteOne
{
public:
ConcreteTwo(void) {}
virtual ~ConcreteTwo(void) {}
};
So are you familiar with the fact that the type of a pointer hardly has anything to do with the type it points at? In other words, where you under the impression that if T1 inherits from T2 that T1* also inherits from T2*? That would be mistaken. Now, how does that apply to your current situation?
InterfaceClass** ptrptr;
ConcreteTwo* object = new ConcreteTwo();
ptrptr = (InterfaceClass**)(&object); // cast is required for some reason.
Here's a major problem with C style casting. OK, so it saves some horizontal space but do you even know what kind of cast you just did? It's not what you think it is. You actually performed a reintpret_cast from type ConcreteTwo* to an unrelated type InterfaceClass*! Now the pointer address has nothing to do with the type you say it is.
Then you toss a reinterpreted pointer type into delete, which promptly caused you to violate your own sphincter.
Well, the compiler warned you, you decided to do it your way...
You can't do this cast:
ptrptr = (InterfaceClass**)(&object);
because object
points to ConcreteTwo
, which is not the same as InterfaceClass
. InterfaceClass
sub-object of ConcreteTwo
is located at a different address. *ptrptr
is not a pointer to instance of InterfaceClass
.
The pointer you pass to delete
is a pointer to ConcreteTwo
, however you said to the compiler that it is a pointer to InterfaceClass
. delete
assumes that it is indeed an InterfaceClass
, hence the crash.
"Converting Derived* → Base* works OK; why doesn't Derived** → Base** work?" in the C++ FAQ Lite.
I think the problem are in the cast lines. BTW, if you remove the cast you inserted the compiler tells you precisely what's the problem.
If you really want to do this, way, which I strongly advise against, you should first create a temporary:
ConcreteTwo* object = new ConcreteTwo();
InterfaceClass* ptr = object;
then you can take its address and assign it to the ptrptr
variable:
InterfaceClass** ptrptr = &ptr;
now you can safely delete it:
delete *ptrptr;
Take into account that if ptr
goes out of scope before ptrptr
, the delete will probably crash again.
As for the rest, Noah explains you why your code is not working.
精彩评论