Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

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();
    }
    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;
}
like image 698
There is nothing we can do Avatar asked Oct 26 '10 12:10

There is nothing we can do


People also ask

When can you not use forward declaration?

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.

Is forward declaration good practice?

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).

What is the point of forward declaration?

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.

Should I use forward declaration or include?

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 .

Can We declare pointers and references from a forward declaration?

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.

What is forward declaration in C++?

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.

How to access the members of a forward declared class?

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.

Is it possible to define class functions within the class declaration?

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).


6 Answers

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.

like image 112
mkaes Avatar answered Oct 02 '22 18:10

mkaes


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.

like image 27
Steve Jessop Avatar answered Oct 02 '22 18:10

Steve Jessop


You cannot delete a pointer to an incomplete type. Delete is one of the operations which requires the type to be complete. HTH

like image 28
Armen Tsirunyan Avatar answered Oct 02 '22 18:10

Armen Tsirunyan


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.

like image 37
Johan Kotlinski Avatar answered Oct 02 '22 17:10

Johan Kotlinski


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.

like image 36
Naveen Avatar answered Oct 02 '22 18:10

Naveen


(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.

like image 22
Jan Avatar answered Oct 02 '22 16:10

Jan