0. Background

Let’s start with demo code, for programmer, I think there is nothing more straight forward than code,

Attention, my environment is,

Linux version 3.17.7-200.fc20.x86_64 (gcc version 4.8.3 20140911 (Red Hat 4.8.3-7) (GCC) )

//FdClass.h
//Defination of Class Fd
#ifndef _FD_CLASS_H
#define _FD_CLASS_H
class Fd
{
public:
Fd(int a);
void Print();
private:
int mem;
};
#endif
//FdClass.cpp
//Impletation of Class Fd
#include "FdClass.h"
#include <stdio.h>;
Fd::Fd(int a):mem(a)
{

};

void Fd::Print()
{
printf("Printing in FdClass, Member: [%d] \n", mem);
return;
}
//ImplClass.h
//Defination of Class Impl
#include "FdClass.h"
class Impl
{
public:
Impl(Fd fd);
~Impl();
int Process();
private:
Fd FdMember;
};
//ImplClass.cpp
//Implementation of Class Impl

#include "ImplClass.h"
#include <stdio.h>;

Impl::Impl(Fd fd):
FdMember(fd)
{

}

int Impl::Process()
{
printf("Processing Impl\n");
FdMember.Print();
return 0;
}

Impl::~Impl()
{

}
//main

#include "ImplClass.h"
int main()
{
Fd fd(100);
Impl xxx(fd);
int ret = xxx.Process();
return 0;
}

After build by gcc,

g++ main.cpp ImplClass.cpp FdClass.cpp -o Fd

The output is,

Processing Impl
Printing in FdClass, Member: [100]

in ImplClass.h, we have defined, intialiazed in constructor and used one member of ‘Fd’ named ‘FdMember’ because of the ‘include’ highlighted, Of course, this is not what I want to share,

What the code will looks like if I use forward declaration instead of ‘include’?

Let’s update the ImplClass.h, ImplClass.cpp & main.cpp to make it work

//ImplClass.h
//Defination of Class Impl
//#include "FdClass.h"
class Fd;
class Impl
{
public:
//  Impl(Fd fd);
Impl(Fd* fd);
~Impl();
int Process();
private:
//  Fd  FdMember;
Fd* FdMember;
};
//ImplClass.cpp
//Implementation of Class Impl

#include "ImplClass.h"
#include "FdClass.h"
#include <stdio.h>;

//Impl::Impl(Fd fd):
Impl::Impl(Fd* fd):
FdMember(fd)
{

}

int Impl::Process()
{
printf("Processing Impl\n");
//  FdMember.Print();
FdMember->Print();
return 0;
}

Impl::~Impl()
{

}
//main
#include "FdClass.h"
#include "ImplClass.h"
int main()
{
Fd fd(100);
//  Impl xxx(fd);
Impl xxx(&fd);
int ret = xxx.Process();
return 0;
}

After build and excute the ./Fd as the same command above, we found everything is OK with the output we expected, and in ‘ImplClass.h’, we used forward declaration instead of ‘include’ as highlighted.

1.What is Forward Declaration

Refer to wiki, https://en.wikipedia.org/wiki/Forward_declaration, and if focus on the ‘class’ part with Cpp, it explains,

In C++, classes can be forward-declared if you only need to use the pointer-to-that-class type (since all object pointers are the same size, and this is what the compiler cares about). This is especially useful inside class definitions, e.g. if a class contains a member that is a pointer to another class; to avoid circular references (i.e. that class might also contain a member that is a pointer to this class), we simply forward-declare the classes instead.

Forward declaration of a class is not sufficient if you need to use the actual class type, for example, if you have a member whose type is that class directly (not a pointer), or if you need to use it as a base class, or if you need to use the methods of the class in a method.

According to this, use this as pointer is a must, what if not? Let me try with ImplClass

//ImplClass.h
//Defination of Class Impl
//#include "FdClass.h"
class Fd;
class Impl
{
public:
Impl(Fd fd);
Impl(Fd* fd);
~Impl();
int Process();
private:
Fd FdMember;
//  Fd* FdMember;
};
//ImplClass.cpp
//Implementation of Class Impl

#include "ImplClass.h"
#include "FdClass.h"
#include <stdio.h>;

Impl::Impl(Fd fd):
//Impl::Impl(Fd* fd):
FdMember(fd)
{

}

int Impl::Process()
{
printf("Processing Impl\n");
FdMember.Print();
//  FdMember->Print();
return 0;
}

Impl::~Impl()
{

}
//main
#include "FdClass.h"
#include "ImplClass.h"
int main()
{
Fd fd(100);
Impl xxx(fd);
//  Impl xxx(&fd);
int ret = xxx.Process();
return 0;
}

And then build it like what I do before,

In file included from ImplClass.cpp:4:0:
ImplClass.h:13:5: error: field ‘FdMember’ has incomplete type
Fd FdMember;
^
ImplClass.cpp: In constructor ‘Impl::Impl(Fd)’:
ImplClass.cpp:10:8: error: class ‘Impl’ does not have any field named ‘FdMember’
FdMember(fd)
^
ImplClass.cpp: In member function ‘int Impl::Process()’:
ImplClass.cpp:18:2: error: ‘FdMember’ was not declared in this scope
FdMember.Print();

As wiki expected, it failed due to the incomplete type definition,

Now let me import one inline setter function which is ‘SetFdMember’ to make it more interesting,

//ImplClass.h
//Defination of Class Impl
//#include "FdClass.h"
class Fd;
class Impl
{
public:
//  Impl(Fd fd);
Impl(Fd* fd);
~Impl();
int Process();
inline void SetFdMember(Fd* fd) { FdMember = fd; };
private:
//  FD FdMember;
FD* FdMember;
};
//main
#include "FdClass.h"
#include "ImplClass.h"
int main()
{
Fd fd(100);
Fd fd2(200);
//  Impl xxx(fd);
Impl xxx(&fd);
xxx.SetFdMember(&fd2);
int ret = xxx.Process();
return 0;
}

Then build as what I did above, everything is fine and then the output is

Processing Impl
Printing in FdClass, Member: [200]

And the setter works fine as we expected.

What if we update this inline setter to a function with one ‘Fd’ instance parameter, and we assign the address to member ‘FdMember’?

//ImplClass.h
//Defination of Class Impl
//#include "FdClass.h"
class Fd;
class Impl
{
public:
// Impl(Fd fd);
Impl(Fd* fd);
~Impl();
int Process();
inline void SetFdMember(Fd fd) { FdMember = &fd; };
// inline void SetFdMember(Fd* fd) { FdMember = fd; };
private:
// Fd FdMember;
Fd* FdMember;
};

And of course, we need to update the setter call in main.cpp

//main
#include "FdClass.h"
#include "ImplClass.h"
int main()
{
Fd fd(100);
Fd fd2(200);
// Impl xxx(fd);
Impl xxx(&fd);
// xxx.SetFdMember(&fd2);
xxx.SetFdMember(fd2);
int ret = xxx.Process();
return 0;
}

Then build it as above, it failed as we expected,

In file included from ImplClass.cpp:4:0:
ImplClass.h: In member function ‘void Impl::SetFdMember(Fd)’:
ImplClass.h:12:14: error: ‘fd’ has incomplete type
inline void SetFdMember(Fd fd) { FdMember = &fd; };
^
ImplClass.h:4:7: error: forward declaration of ‘class Fd’
class Fd;
^

But what will happen if we update the code in ImplClass.h, make the setter pass the parameter by reference, like this:

//ImplClass.h
//Defination of Class Impl
//#include "FdClass.h"
class Fd;
class Impl
{
public:
// Impl(Fd fd);
Impl(Fd* fd);
~Impl();
int Process();
inline void SetFdMember(Fd& fd) { FdMember = &fd; };
// inline void SetFdMember(Fd* fd) { FdMember = fd; };
private:
// Fd FdMember;
Fd* FdMember;
};

It passed!!, and the output is what we expected!

Processing Impl Printing in FdClass, Member: [200]
2. How Forward Declaration Implemented

To make it clear about this, we need to have the common knowledge of difference of parameter by reference and parameter by value in function call.

1, Parameter by value, which means we will have one new instance copy constructed at the very beginning of this function is called,

inline void SetFdMember(Fd fd) { //To do };

The will be one instance named ‘fd’ of ‘Fd’ in the scope of function ‘SetFdMember’, which will fully copy constructed before the code in the function is executed. But if ‘Fd’, is only forward declared rather than included, which means we have no ‘Fd’ defination. In this case, there will be no constructor for Fd, and also no requirement of memory that should be allocated for fd.

2. Parameter by reference. Against parameter by value, no instance need to be constructed, we just use and reference, which means, just the instance passed into when called, also,

 inline void SetFdMember(Fd& fd) { FdMember = &fd; }; 

we have only one address assignment to the pointer, which means we just get the address of parameter and give it to this ‘FdMember’, there is nothing more needed other than assign one pointer by address.

It seems to make some sense, and could explain what have happened, but the problem is still there:

How forward declaration implemented?

As what we have done before, let’s try some code at first,

//ImplClass.h
//Defination of Class Impl
//#include "FdClass.h"
//class Fd;
class Impl
{
public:
//    Impl(Fd fd);
Impl(void* fd);
~Impl();
int Process();
inline void SetFdMember(void* fd) { FdMember = fd; };
//    inline void SetFdMember(Fd* fd) { FdMember = fd; };
private:
//    Fd    FdMember;
void*    FdMember;
};

We used ‘void*’ instead of ‘include’/forward declaration, now we don’t give a shit to anything about ‘include’/forward declaration, both of them, go to hell. Pointer could be coding lord, I think all the programmers start from C know very clear about that.

But before we build it, ImplClass.cpp and main.cpp need to be updated along with this change,

//ImplClass.cpp
//Implementation of Class Impl

#include "ImplClass.h"
#include "FdClass.h"
#include <stdio.h>

//Impl::Impl(Fd fd):
Impl::Impl(void* fd)//:
// FdMember(fd)
{
FdMember = (Fd*)fd;
}

int Impl::Process()
{
printf("Processing Impl\n");
// FdMember.Print();
((Fd*)(FdMember))->Print();
return 0;
}

Impl::~Impl()
{

}
//main
#include "FdClass.h"
#include "ImplClass.h"
int main()
{
Fd fd(100);
Fd fd2(200);
//    Impl xxx(fd);
Impl xxx(&fd);
//    xxx.SetFdMember(&fd2);
xxx.SetFdMember(&fd2);
int ret = xxx.Process();
return 0;
}

It passed the build, and give us the result what we expect.
Yes, now what is in your mind is real, that is
Forward declaration is nothing!
it is just like ‘void*’, the compiler does not know anything about it because there is no definition, just a symbol that let compiler know this one exists, all the implementation are gotten only when we link all the ‘.o'(object) files/’.a'(static lib)/’.so'(dynamic lib).

But compared to ‘void*’, let us try the following code,

//main
#include "FdClass.h"
#include "ImplClass.h"
int main()
{
Fd fd(100);
//    Fd fd2(200);
char fd2(0xff);
//    Impl xxx(fd);
Impl xxx(&fd);
//    xxx.SetFdMember(&fd2);
xxx.SetFdMember(&fd2);
int ret = xxx.Process();
return 0;
}

and updated the output by Hex,

//FdClass.cpp
//Impletation of Class Fd
#include "FdClass.h"
#include <stdio.h>
Fd::Fd(int a):mem(a)
{

};

void Fd::Print()
{
printf("Printing in FdClass, Member: [%X] \n", mem);
return;
}

Then, build and run

Processing Impl
Printing in FdClass, Member: [64FF]

Obviously, this code in main has one bug, that we need one ‘Fd’ instance to be the input parameter of the setter, but here fd2 is a char one. We passed the build and there is not even a warning due to the use of ‘void*’ and casts. Of course the output value is not valid in this case, in some other case, the program would even crash sometime.
That is another reason why forward declaration is more recommended than ‘void*’. If we are using forward declaration, the program would not even pass the build, error would received and we can target where the bug is.

This is very important for debug. Potential risk of the bugs that could only be found in runtime really sucks, and after spare a entry week debugging, analyze dozen Gs log, review every line int code tree, finally found the bug is caused by this ‘void*’, I promise, you will check all the commit logs on svn/git to find out the author and put a bullet into his head if there is a rifle besides.

Why the output is ’64FF’? It is another long story, I think there will be an answer if we have a concept of ‘sizeof(char)’ and ‘sizeof(int)’.

Now let us try the forward declaration case, of course, before that, we have to revert ‘ImplClass.h’, ‘ImplClass.cpp’ back to forward declaration ones,

//main
#include "FdClass.h"
#include "ImplClass.h"
int main()
{
Fd fd(100);
//    Fd fd2(0xFF);
char fd2(0xFF);
//    Impl xxx(fd);
Impl xxx(&fd);
//    xxx.SetFdMember(&fd2);
xxx.SetFdMember(&fd2);
int ret = xxx.Process();
return 0;
}
//ImplClass.h
//Defination of Class Impl
//#include "FdClass.h"
class Fd;
class Impl
{
public:
Impl(Fd *fd);
//    Impl(void* fd);
~Impl();
int Process();
//    inline void SetFdMember(void* fd) { FdMember = fd; };
inline void SetFdMember(Fd* fd) { FdMember = fd; };
private:
Fd*    FdMember;
//    void*    FdMember;
};
//ImplClass.cpp
//Implementation of Class Impl

#include "ImplClass.h"
#include "FdClass.h"
#include <stdio.h>

Impl::Impl(Fd* fd):
//Impl::Impl(void* fd)//:
FdMember(fd)
{
//    FdMember = (Fd*)fd;
}

int Impl::Process()
{
printf("Processing Impl\n");
//    FdMember.Print();
//    ((Fd*)(FdMember))->Print();
FdMember->Print();
return 0;
}

Impl::~Impl()
{

}

Then build it, Oops, fails as we expected:

main.cpp: In function ‘int main()’:
main.cpp:12:22: error: no matching function for call to ‘Impl::SetFdMember(char*)’
xxx.SetFdMember(&fd2);
^
main.cpp:12:22: note: candidate is:
In file included from main.cpp:3:0:
ImplClass.h:13:14: note: void Impl::SetFdMember(Fd*)
inline void SetFdMember(Fd* fd) { FdMember = fd; };
^
ImplClass.h:13:14: note:   no known conversion for argument 1 from ‘char*’ to ‘Fd*’

Yes, that is what we want to get, the bug would be quite clear at the time we build it, which means, this bug will never exist unless un-builded code is permitted commit. Also, 7-8 bucks is saved if your rifle is AK47-_-!

3. Summary

1, Compared with included, forward declaration has benefits as wiki mentioned or you can just google it.

2, In fact, forward declaration is nothing more than ‘void*’. Although it could be used in varies ways, default usage is preferred except there is dedicated requirement.

3, Compared with ‘void*’, forward declaration is more readable. It is of fundamental importance for programmer that code must be easy to read and understood. Always, co-workers weight more than compiler, customer or pm.

4, Compared with ‘void*’, what’s most important, bugs could be targeted during building time instead of during/after runtime, which really means a lot also for the maintenance and support.

Advertisements