pointer of pointer array

Below is a working program, compiled with g++.
But due to an unavoidable reason, I need to define class Human before class Elephant. The program fails, even after forward declaration of class Elephant before class Human.


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
// This program works fine.
#include <iostream>
#define ELEPHANTpopulation  3
#define HUMANpopulation     5

using namespace std;


class Elephant
{
    private:
        int age;

    public:
        int getAge()         { return this->age; }
        void setAge(int age) { this->age = age; }
};


class Human
{
    private:
        int age;

    public:
        int getAge()          { return this->age; }
        void setAge(int age)  { this->age = age; }

        void readElephantAge( Elephant ** p )
        {
            for (int i=0; i < ELEPHANTpopulation; i++)
                cout << " The age of elephant-" << i <<" is " << p[i]->getAge() << endl;
        }
};


int main()
{
    cout << "Progam Start !" << endl;
    Human * m [ HUMANpopulation ];
    for (int i=0;  i < HUMANpopulation;  i++)
    {
        m [i] = new Human();
        m [i]->setAge(40);
    }

    Elephant *p[ ELEPHANTpopulation ];
    for (int i=0; i < ELEPHANTpopulation; i++)
    {
        p[i] = new Elephant();
        p[i]->setAge(5);
    }

    for (int i=0; i < HUMANpopulation; i++)
    {
        cout << "Person-" << i << " can read this:" << endl;
        m [i]->readElephantAge( p );
        cout << endl;
    }
}


Now I need to define class Human before class Elephant.
After modified as below, the program fails to compile, even though forward declaration had already been included.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
// This program fails to compile.
#include <iostream>
#define ELEPHANTpopulation  3
#define HUMANpopulation     5

using namespace std;

class Elephant;

class Human
{
    private:
        int age;

    public:
        int getAge()          { return this->age; }
        void setAge(int age)  { this->age = age; }

        void readElephantAge( Elephant ** p )
        {
            for (int i=0; i < ELEPHANTpopulation; i++)
                cout << " The age of elephant-" << i <<" is " << p[i]->getAge() << endl;
        }
};


class Elephant
{
    private:
        int age;

    public:
        int getAge()         { return this->age; }
        void setAge(int age) { this->age = age; }
};



int main()
{
    cout << "Progam Start !" << endl;
    Human * m [ HUMANpopulation ];
    for (int i=0;  i < HUMANpopulation;  i++)
    {
        m [i] = new Human();
        m [i]->setAge(40);
    }

    Elephant *p[ ELEPHANTpopulation ];
    for (int i=0; i < ELEPHANTpopulation; i++)
    {
        p[i] = new Elephant();
        p[i]->setAge(5);
    }

    for (int i=0; i < HUMANpopulation; i++)
    {
        cout << "Person-" << i << " can read this:" << endl;
        m [i]->readElephantAge( p );
        cout << endl;
    }
}


Question: Why the forward declaration of class Elephant doesn't work?
Last edited on
When the compiler compiles statement

cout << " The age of elephant-" << i <<" is " << elephant[i]->getAge() << endl;

of member function Human::readElephantAge it does not know that class Elephant contains member getAge. It only knows that there is class Elephant but knows nothing what members it has. So the compiler issues an error.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
class Elephant;
 
class Human
 {
 private:
 int age;
 
public:
 int getAge() { return age; }
 void setAge( int age ) { this->age = age; }
 
void readElephantAge( Elephant ** elephant )
 {
 for (int i=0; i < ELEPHANTpopulation; i++)
 cout << " The age of elephant-" << i <<" is " << elephant[i]->getAge() << endl;
 }
 };
 
Last edited on
Vlad is correct.

The standard C++ way is to put the Elephant class definition into a header file, Elephant.h. You don't have to put the full definitions of the methods in there, just their prototypes, although you can if you prefer.

Then the any files which contain the code which calls methods of Elephant should include that header, so that when the compiler compiles that code, it knows what methods exist in Elephant.

In fact, the normal way to do it would be to define each class in its own header file (foo.h), and the class's methods in its own source file (foo.cpp). Then let the linker link them together.

Also, when posting code here, please could you enclose it in code tags, to make it easier to read?
Last edited on
Dear Vlad and MikeyBoy,

Thank you very much for your kindness and attention.

I have followed your advice, separating the program into separate files, the compilation gives error: 'Elephant' has not been declared.
Last edited on
It is obvious that file human.h shall contain file elephant.h or at least the parameter in function readElephantAge should be defined with keyword class

void readElephantAge( class Elephant ** );

Also the human.cpp shall include elephant.h.
Last edited on
Dear vlad,

Thanks, it works !
To summarize, the keys to make it works are:

i) In cases like this, we have to separate the program into appropriate files, i.e. *.h, *.cpp accordingly.

ii) Both files of "human.h" and "elephant.h" should include each other.

iii) The keyword "class" is needed in both files of "human.h" and "elephant.h"

Note: In "main.cpp" we only need to include either "human.h" or "elephant.h", because either of these already includes the other one. Of course we can also include both "human.h" and "elephant.h" in the main.cpp

To share with others who may be interested, the working program complete files are as below:

File 1: main.cpp
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
#include "human.h"

#include <iostream>
using namespace std;


int main()
{
    cout << "Progam Start !" << endl << endl;
    Human * m[ HUMANpopulation ];
    for (int i=0;  i < HUMANpopulation;  i++)
    {
        m[i] = new Human();
        m[i]->setAge(40);
    }

    Elephant *p[ ELEPHANTpopulation ];
    for (int i=0; i < ELEPHANTpopulation; i++)
    {
        p[i] = new Elephant();
        p[i]->setAge(5);
    }

    for (int i=0; i < HUMANpopulation; i++)
    {
        cout << "Person-" << i+1 << " can read this:" << endl;
        m[i]->readElephantAge( p );
        cout << endl;
    }

    cout << endl << endl;

    for (int i=0; i < ELEPHANTpopulation; i++)
    {
        cout << "Elephant-" << i+1 << " can read this:" << endl;
        p[i]->readHumanAge( m );
        cout << endl;
    }

}


File 2: elephant.h
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
#ifndef ELEPHANT_H_INCLUDED
#define ELEPHANT_H_INCLUDED

#include "human.h"                          // need to include "human.h" here

#define ELEPHANTpopulation  3

class Elephant
{
    private:
        int age;

    public:
        int getAge();
        void setAge(int);
        void readHumanAge( class Human ** );           // keyword "class" is needed here
};

#endif // ELEPHANT_H_INCLUDED 


File 3: elephant.cpp
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
#ifndef ELEPHANT_CPP_INCLUDED
#define ELEPHANT_CPP_INCLUDED

#include "elephant.h"

#include <iostream>
using namespace std;

int Elephant::getAge()
{
    return age;
}


void Elephant::setAge( int age )
{
    this->age = age;
}


void Elephant::readHumanAge( Human ** m )
{
    for (int i=0; i < HUMANpopulation; i++)
        cout << " The age of human-" << i+1 <<" is " << m[i]->getAge() << endl;
}

#endif // ELEPHANT_CPP_INCLUDED 


File 4: human.h
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
#ifndef HUMAN_H_INCLUDED
#define HUMAN_H_INCLUDED

#include "elephant.h"                                     // need to include "elephant.h" here

#define HUMANpopulation     5

class Human
{
    private:
        int age;

    public:
        int getAge();
        void setAge(int);
        void readElephantAge( class Elephant ** );               // keyword "class" is needed here
};

#endif // HUMAN_H_INCLUDED 


File 5: human.cpp
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
#ifndef HUMAN_CPP_INCLUDED
#define HUMAN_CPP_INCLUDED

#include "human.h"

#include <iostream>
using namespace std;

int Human::getAge()
{
    return age;
}


void Human::setAge(int age)
 {
     this->age = age;
 }


void Human::readElephantAge( Elephant ** p )
{
    for (int i=0; i < ELEPHANTpopulation; i++)
        cout << " The age of elephant-" << i+1 <<" is " << p[i]->getAge() << endl;
}


#endif // HUMAN_CPP_INCLUDED 

Last edited on
activecat wrote:
i) Both files of "human.h" and "elephant.h" should include each other.

None of them need to include the other. When human.h includes elephant.h it will not include the content of human.h because of the header guard but the code compiles so that must mean that elephant.h doesn't need to include human.h. The same when elephant.h includes human.h, so they do not need to include each other.
Last edited on
Dear Peter87,

I am using Code::Blocks 10.05 with g++ compiler, I add all the 5 files above into a project and compile it.
The program works fine if both elephant.h and human.h include each other.

But when I remove #include elephant.h from human.h, the compilation produces 3 errors.

One of the errors is
error: forward declaration of 'struct Elephant' in the human.h file.

If not include each other, is there any other way to solve the error..?
Last edited on
And another strange thing is, even with both elephant.h and human.h include each other, the program fails to compile when using g++ command.

Procedure:
1. Put all the 5 files above into same directory, says ~/test01
2. cd ~/test01; g++ main.cpp

The compilation fails with following message:

/tmp/ccTqlEEJ.o: In function `main':
main.cpp:(.text+0x69): undefined reference to `Human::setAge(int)'
main.cpp:(.text+0xb3): undefined reference to `Elephant::setAge(int)'
main.cpp:(.text+0x11d): undefined reference to `Human::readElephantAge(Elephant**)'
main.cpp:(.text+0x1b2): undefined reference to `Elephant::readHumanAge(Human**)'
collect2: ld returned 1 exit status


But why fails to compile with g++ command?
Last edited on
If you remove #include "human.h" from elephant.h you will have to add it to elephant.cpp and main.cpp and if you remove #include "human.h" from elephant.h you will have to add it to human.cpp. I think this is better because all files includes only what they need and nothing more.

All source files have to be compiled before it can be linked into a program. To compile a single file you can use the -c flag.
1
2
3
g++ -c human.cpp
g++ -c elephant.cpp
g++ -c main.cpp
or you can do it all in one line
g++ -c human.cpp elephant.cpp main.cpp
This will create three object files (.o). Now we can link everything together into an executable file.
g++ -o nameOfExecutableFile human.o elephant.o main.o
-o nameOfExecutableFile is optional but can be used to give a better name to the executable file. It is possible to do compilation and linking at the same time by passing the source files directly. This is what you did but you forgot to pass human.cpp and elephant.cpp.
g++ -o nameOfExecutableFile human.cpp elephant.cpp main.cpp
And if we are lazy we can just use a wildcard to compile and link all source files in the current directory.
g++ -o nameOfExecutableFile *.cpp
If human.cpp, elephant.cpp and main.cpp is in the current directory this will expand to the same thing we had previously.
Last edited on
Dear Peter87,

Yes, it all work !
Below is what I have tested.

Step 1):
human.h does not need to include elephant.h
elephant.h does not need to include human.h

Step 2):
In human.cpp, we need to include elephant.h, and
In elephant.cpp, we need to include human.h

Step 3):
In main.cpp, we need to include both human.h and elephant.h

When step 1 to 3 above are performed, then the program works fine.

Meanwhile, what you said about the g++ also work !
You are brilliant !!

Thank you.
Last edited on
The final output of the program is as below:
(The human objects can access elephant objects, vice-versa, through the pointer of pointer array)


Progam Start !

Person-1 can read this:
 The age of elephant-1 is 5
 The age of elephant-2 is 5
 The age of elephant-3 is 5

Person-2 can read this:
 The age of elephant-1 is 5
 The age of elephant-2 is 5
 The age of elephant-3 is 5

Person-3 can read this:
 The age of elephant-1 is 5
 The age of elephant-2 is 5
 The age of elephant-3 is 5

Person-4 can read this:
 The age of elephant-1 is 5
 The age of elephant-2 is 5
 The age of elephant-3 is 5

Person-5 can read this:
 The age of elephant-1 is 5
 The age of elephant-2 is 5
 The age of elephant-3 is 5



Elephant-1 can read this:
 The age of human-1 is 40
 The age of human-2 is 40
 The age of human-3 is 40
 The age of human-4 is 40
 The age of human-5 is 40

Elephant-2 can read this:
 The age of human-1 is 40
 The age of human-2 is 40
 The age of human-3 is 40
 The age of human-4 is 40
 The age of human-5 is 40

Elephant-3 can read this:
 The age of human-1 is 40
 The age of human-2 is 40
 The age of human-3 is 40
 The age of human-4 is 40
 The age of human-5 is 40





To recap, the code to produce above result are:

File 1: main.cpp

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
#include "human.h"
#include "elephant.h"

#include <iostream>
using namespace std;


int main()
{
    cout << "Progam Start !" << endl << endl;
    Human * m[ HUMANpopulation ];
    for (int i=0;  i < HUMANpopulation;  i++)
    {
        m[i] = new Human();
        m[i]->setAge(40);
    }

    Elephant *p[ ELEPHANTpopulation ];
    for (int i=0; i < ELEPHANTpopulation; i++)
    {
        p[i] = new Elephant();
        p[i]->setAge(5);
    }

    for (int i=0; i < HUMANpopulation; i++)
    {
        cout << "Person-" << i+1 << " can read this:" << endl;
        m[i]->readElephantAge( p );
        cout << endl;
    }

    cout << endl << endl;

    for (int i=0; i < ELEPHANTpopulation; i++)
    {
        cout << "Elephant-" << i+1 << " can read this:" << endl;
        p[i]->readHumanAge( m );
        cout << endl;
    }

}



File 2: elephant.h

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
#ifndef ELEPHANT_H_INCLUDED
#define ELEPHANT_H_INCLUDED

// no need to include human.h

#define ELEPHANTpopulation  3

class Elephant
{
    private:
        int age;

    public:
        int getAge();
        void setAge(int);
        void readHumanAge( class Human ** );     // keyword class could be omitted in this file
};

#endif // ELEPHANT_H_INCLUDED



File 3: elephant.cpp

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
#ifndef ELEPHANT_CPP_INCLUDED
#define ELEPHANT_CPP_INCLUDED

#include "human.h"
#include "elephant.h"

#include <iostream>
using namespace std;

int Elephant::getAge()
{
    return age;
}


void Elephant::setAge( int age )
{
    this->age = age;
}


void Elephant::readHumanAge( Human ** m )
{
    for (int i=0; i < HUMANpopulation; i++)
        cout << " The age of human-" << i+1 <<" is " << m[i]->getAge() << endl;
}

#endif // ELEPHANT_CPP_INCLUDED



File 4: human.h

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
#ifndef HUMAN_H_INCLUDED
#define HUMAN_H_INCLUDED

// no need to include elephant.h

#define HUMANpopulation     5

class Human
{
    private:
        int age;

    public:
        int getAge();
        void setAge(int);
        void readElephantAge( class Elephant ** );   // keyword class is needed in this file
};

#endif // HUMAN_H_INCLUDED



File 5: human.cpp

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
#ifndef HUMAN_CPP_INCLUDED
#define HUMAN_CPP_INCLUDED

#include "human.h"
#include "elephant.h"

#include <iostream>
using namespace std;

int Human::getAge()
{
    return age;
}


void Human::setAge(int age)
{
     this->age = age;
}


void Human::readElephantAge( Elephant ** p )
{
    for (int i=0; i < ELEPHANTpopulation; i++)
        cout << " The age of elephant-" << i+1 <<" is " << p[i]->getAge() << endl;
}


#endif // HUMAN_CPP_INCLUDED

Last edited on
activecat,

The important thing to understand is that you need to include the header file somewhere before the bit of code that needs to know the class definition.

So, for example, your file elephant.cpp needs to know about the definition of Elephant (obviously, since it is defining the behaviour of that class), and it needs to know the definition of Human as it is calling a method on than class (Human::age() ).

By the same reasoning, human.cpp needs to include both headers too.

elephant.h doesn't need to know anything about the Human class. While it uses the type name Human, the only thing it does with it is declare a pointer to it. The only thing it needs to know is that Human is a class, so you can get away with a forward declaration in elephant.h, and not needing to include the definition of Human.

All of this is separate from the linker issues, which Peter discussed above
Again, the same reasoning holds true for human.h not needing to include elephant.h.

I must admit, I've not seen the class keyword used the way you're using it, in the method argument definition, before. The more usual thing to do would be to use a forward declaration, as you do in your OP.

Dear Mikey Boy,

Thanks for the generalization.

Let's refer back to the last update of the source code.
Could you explain, why the "class" keyword could be omitted in elephant.h, but could not be omitted in human.h ?

In elephant.h: Omitting below "class" keyword is ok.
void readHumanAge( class Human ** );

In human.h: Omitting below "class" keyword will cause compilation error !
void readElephantAge( class Elephant ** );

Why there is such a "double standard" ..?
Last edited on
Dear Mikey Boy,

Another question is, when all the 5 files are combined into a single file, it doesn't compile.
Refer below main.cpp:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
#include <iostream>
using namespace std;

#define HUMANpopulation     5
#define ELEPHANTpopulation  3

class Elephant;    // forward declaration

class Human
{
    private:
        int age;

    public:
        int getAge();
        void setAge(int);
        void readElephantAge(  class Elephant ** );  
};

int Human::getAge()
{
    return age;
}

void Human::setAge(int age)
{
     this->age = age;
}

void Human::readElephantAge( Elephant ** p )
{
    for (int i=0; i < ELEPHANTpopulation; i++)
        cout << " The age of elephant-" << i+1 <<" is " << p[i]->getAge() << endl;   // compilation error here: 
                                                                                     // invalid use of incomplete type 'struct Elephant'
}



class Elephant
{
    private:
        int age;

    public:
        int getAge();
        void setAge(int);
        void readHumanAge( class Human ** );    
};

int Elephant::getAge()
{
    return age;
}

void Elephant::setAge( int age )
{
    this->age = age;
}

void Elephant::readHumanAge( Human ** m )
{
    for (int i=0; i < HUMANpopulation; i++)
        cout << " The age of human-" << i+1 <<" is " << m[i]->getAge() << endl;
}


int main()
{
    cout << "Progam Start !" << endl << endl;
    Human * m[ HUMANpopulation ];
    for (int i=0;  i < HUMANpopulation;  i++)
    {
        m[i] = new Human();
        m[i]->setAge(40);
    }

    Elephant *p[ ELEPHANTpopulation ];
    for (int i=0; i < ELEPHANTpopulation; i++)
    {
        p[i] = new Elephant();
        p[i]->setAge(5);
    }

    for (int i=0; i < HUMANpopulation; i++)
    {
        cout << "Person-" << i+1 << " can read this:" << endl;
        m[i]->readElephantAge( p );
        cout << endl;
    }

    cout << endl << endl;

    for (int i=0; i < ELEPHANTpopulation; i++)
    {
        cout << "Elephant-" << i+1 << " can read this:" << endl;
        p[i]->readHumanAge( m );
        cout << endl;
    }

}


The above code produces below compilation error at line 33:

error: invalid use of incomplete type 'struct Elephant'


Does this mean that there is no way to put all code into a single file..?
Last edited on
To repeat what's been said earlier:

You need to have the definition of the Elephant class before the point where the code needs to use it. If the compiler encounters an attempt to use a method of Elephant before it's encountered the declaration of that method, it will fail.
Thanks.
So the conclusion is, for cases like this, the separation of source code into few files are just unavoidable.
Last edited on
It's not unavoidable. You just need to move the definition of the Elephant class to before the point where any code needs to use it.

Which is effectively the same thing as what happens when you include elephant.h at the top of your source file - you're including the class definition before the code needs to use it.

It's usually better to break your project up into individual files. But it's not necessary here, if you have any special need to keep it all in once file.
Could you explain, why the "class" keyword could be omitted in elephant.h, but could not be omitted in human.h ?


Sorry, didn't see this earlier. It's because, in all the places where you include elephant.h, you've happened to include human.h before it. So by the time the compiler gets compiling the contents of elephant.h, it's already seen a definition of Human, and so doesn't need to be told that it's a class.

If you'd happened to include the header files the other way around, the reverse would be true.

Edit: The sensible thing to do is not to make any assumptions about the order in which other files will include the header. Both header files should contain the appropriate forward declarations, so that they can be re-used independently or in any order.
Last edited on
Topic archived. No new replies allowed.