Forward declaration just won't do
Below are two fragments (ready to compile) of code. In first fragment in which I'm using only forward declaration for a struct while deleting pointer to this struct from a Base class dtor for a Guest class isn't invoked.
In the second fragment when instead of forward declaration I use full definition of this Guest class using delete in Base works ase intended. Why? Why does it make a difference? Isn't forward declaration suppose to be just a note for a compiler saying that the definition of this class/struct is somewhere else? I'm very surprised that it just doesn't work intuitively.//First just forward dclr
#include "stdafx.h"
#include <iostream>
using std::cout;
struct Guest;
struct Base
{
Guest* ptr_;
Base(Guest* ptr):ptr_(ptr)
{
cout << "Base\n";
}
~Base()
{
cout << "~Base\n";
delete ptr_;
}
};
struct Guest
{
Guest()
{
cout << "Guest\n";
throw std::exception();
}
Guest(int)
{
cout << "Guest(int)\n";
}
~Guest()
{
cout << "~Guest\n";
}
};
struct MyClass : Base
{
Guest g;
MyClass(Guest* g):Base(g)
{
cout << "MyClass\n";
}
~MyClass()
{
cout << "~MyClass\n";
}
};
int _tmain(int argc, _TCHAR* argv[])
{
try
{
Guest* g = new Guest(1);
MyClass mc(g);
}
catch(const std::exception& e)
{
std::cerr << e.what();
}
return 0;
}
//Second - full def
#include "stdafx.h"
#include <iostream>
using std::cout;
struct Guest
{
Guest()
{
cout << "Guest\n";
throw std::exception();开发者_JAVA百科
}
Guest(int)
{
cout << "Guest(int)\n";
}
~Guest()
{
cout << "~Guest\n";
}
};
struct Base
{
Guest* ptr_;
Base(Guest* ptr):ptr_(ptr)
{
cout << "Base\n";
}
~Base()
{
cout << "~Base\n";
delete ptr_;
}
};
struct MyClass : Base
{
Guest g;
MyClass(Guest* g):Base(g)
{
cout << "MyClass\n";
}
~MyClass()
{
cout << "~MyClass\n";
}
};
int _tmain(int argc, _TCHAR* argv[])
{
try
{
Guest* g = new Guest(1);
MyClass mc(g);
}
catch(const std::exception& e)
{
std::cerr << e.what();
}
return 0;
}
From the C++ standard (5.3.5/5):
If the object being deleted has incomplete class type at the point of deletion and the complete class has a non-trivial destructor or a deallocation function, the behavior is undefined.
So you cannot use delete on your incomplete type. It would call the destructor and the compiler is not yet aware of it.
Informally: the compiler needs the class definition in order to delete the object correctly, because it needs to know how to call the destructor and/or operator delete
for that class.
Formally, 5.3.5/5:
If the object being deleted has incomplete class type at the point of deletion and the complete class has a non-trivial destructor or a deallocation function, the behavior is undefined.
You'd be OK if (for example) Guest
was POD, but you gave it a destructor, so you're not OK.
You cannot delete a pointer to an incomplete type. Delete is one of the operations which requires the type to be complete. HTH
You can't delete the Guest unless you know its definition. It's destructor won't be called. Also, if Guest has defined a custom operator delete, it would be ignored.
The type of ptr_
is incomplete when you invoke delete
on it. This leads to undefined behavior. So your destructor may not be called. You can use Boost.checked_delete to avoid such scenarios.
(The stdafx.h header is not standard c++.) If I compile with g++, the compiler generates:
warning: possible problem detected in invocation of delete operator:
warning: invalid use of incomplete type ‘struct Guest’
warning: forward declaration of ‘struct Guest’
note: neither the destructor nor the class-specific operator delete will be called, even if they are declared when the class is defined.
Configure your compiler to compile at proper warning and error levels.
精彩评论