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();
}
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;
}
You can not use the forward-declared classes methods in the header file because C++ does not know the definition of that class at that point yet.
The Google style guide recommends against using forward declarations, and for good reasons: If someone forward declares something from namespace std, then your code exhibits undefined behavior (but will likely work).
The first line is what's called a forward declaration. It brings the name of the class into the current namespace without actually defining it.
As the name itself implies, forward declaration is just a Declaration and not a definition. So, you will declare saying the compiler that it is a class and I just declaring it here and will provide you the definition when am gonna use it. So, normally you forward declare in the Header file and #include in the .
You can declare pointers and references from a forward declaration but you can not declare an instance of the class because the type is incomplete, the compiler only knows its name not its structure. You can only declare an instance of the class once the class has been fully declared.
Forward Declaration refers to the beforehand declaration of the syntax or signature of an identifier, variable, function, class, etc. prior to its usage (done later in the program). Example: In C++, Forward declarations are usually used for Classes.
It is important to note that you cannot access the members of solely forward-declared class, you must only try to access members in the .cpp file. Forward declaration will get everything to compile, but all the logic must be done in the .cpp file.
You can define class functions within the class declaration but those functions are automatically inlined so you should only do this for functions with a few lines of code (and that don't use a forward declared type).
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.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With