views:

134

answers:

5

I understand that wherever possible we shall use forward declarations instead of includes to speed up the compilation.

I have a class Person like this.

#pragma once

#include <string>

class Person
{
public:
    Person(std::string name, int age);
    std::string GetName(void) const;
    int GetAge(void) const;
private:
    std::string _name;
    int _age;
};

and a class Student like this

#pragma once

#include <string>

class Person;

class Student
{
public:
    Student(std::string name, int age, int level = 0);
    Student(const Person& person);
    std::string GetName(void) const;
    int GetAge(void) const;
    int GetLevel(void) const;
private:
    std::string _name;
    int _age;
    int _level;
};

In Student.h, I have a forward declaration class Person; to use Person in my conversion constructor. Fine. But I have done #include <string> to avoid compilation error while using std::string in the code. How to use forward declaration here to avoid the compilation error? Is it possible?

Thanks.

+1  A: 

You can only use forward declarations for type identification, such as when you use the declared type in a function/method pointer parameter prototype. If you are going to declare a member variable (i.e. std::string _name;) the compiler needs a little bit more than a forward declaration can give it. For example, if someone does a sizeof(Student) the compiler has to have access to the entire declaration to figure out the size.

Amardeep
@Peter, @Amardeep - Do you mean that this `class Person;` forward declaration won't hold good if there is a member for the class `Person` in the `Student` class?
bdhar
@Amar, sorry, I edited your answer instead of mine :-( now rolled back.
Péter Török
@Péter: No problem at all!
Amardeep
@bdhar: Sorry about my prior comment-response... I misunderstood your question. The answer is affirmative: If Student has a member of type Person then you must include "Person.h".
Amardeep
+2  A: 

You can only use forward declarations with pointers and references (because these are of a fixed size, independent of the size of the object they refer to). If you use a specific class by value, the compiler needs its full definition (in order to know its exact size), thus forward declaration is not enough.

Thus, if you define Student like this

class Student
{
public:
    ...
    Student(const Person person);
    ...
private:
    Person person;
};

either of the above class members would force you to #include Person.h in your header.

You understand well, that forward declaration helps avoiding compilation dependencies, thus reduces compilation time. However, that concerns mainly user-defined headers, because these are the ones that can change frequently. Standard library headers are not going to change, so the saving is not significant in those cases.

Péter Török
@bdhar, regarding your comment above, see my update.
Péter Török
In fact, `GetPerson()` would not force you to include Person.h. The compiler does not need to know the size of the return type of a function in its declaration.
Gorpik
@Gorpik, oops, my bad. Fixed, thanks :-)
Péter Török
+3  A: 

Since used string as

std::string _name;
//^^^^^^^^^ concrete member    

the whole structure of string would be needed, so the declaration must be needed. You must #include <string>.


Declaration of string can be omitted possible if you write, e.g.

std::string* _name;
//^^^^^^^^^^ pointer or reference

which you could use a forward declaration, but I still recommend you not to do so, because std::string is not a simple structure type like Person or Student, but a very complex type involving many templates:

template<class charT, class traits = char_traits<charT>, class Allocator = allocator<charT> >
class basic_string { ... };
typedef basic_string<char> string;

If you forward declare it wrongly (e.g. class string;), the compilation will fail when you actually use it because of conflicting type.

KennyTM
Similarly, if I pass `Person` by value, isn't the forward declaration `class Person;` sufficient?
bdhar
@bdhar: No. ___
KennyTM
@KennyTM, Actually no, if you declare a function that passes an instance of class A by value, or returns it by value, and don't call any member functions on it, forward declaration *is* sufficient, because size of the class is not needed by the compiler.
Alex B
A: 

Maybe also consider to use inheritance.

#include "Person.h" // i am afraid you need to

class Student : public Person
{
     ...

     protected:
         int _level;
         // no name and age, that is inhertianced from person
         // (if you want to access them from student, you maybe need to make them protected)
 };
VDVLeon
Yeah sure. Thanks! But, that's a different topic altogether. I just contrived this example just to make my query clear.
bdhar
A: 

Forward declarations are used to reduce include and to resolve cyclic dependencies. But when using forwards in the public or protected interface of your class, you place the burden of including the correct header files to the users of your class. Beside this, forwarding types defined in the STL may not work as expected. For example, std::string might be defined as typedef basic_string<char> string;. Declaring it forward with namespace std{ class string;} will give you a compilation error.

jopa