I'm new to C++ but I do have some experience in Java. While coding, I stumbled across an error that confused me. Here's my code (simplified, but the errors are the same):
A.h:
#pragma once
#include "B.h"
class A
{
public:
A();
void foo();
void sayHello();
B b;
};
A.cpp:
#include "A.h"
#include <iostream>
A::A() {}
void A::foo() {
b.bar(this);
}
void A::sayHello() {
std::cout << "Hello" << std::endl;
}
B.h:
#pragma once
#include "A.h"
class B
{
public:
B();
void bar(A *a);
};
B.cpp:
#include "B.h"
B::B(){}
void B::bar(A *a) {
a->sayHello();
}
I want to pass a pointer of the a object to the bar function in B, so that I'll be able to modify and access a's fields in bar. Strangely, I get these errors when I call foo through an instance of A from another class:
1>------ Build started: Project: Test, Configuration: Debug Win32 ------
1> main.cpp
1>d:\stuff\visual studio 2015\projects\test\test\b.h(7): error C2061: syntax error: identifier 'A'
1> B.cpp
1>d:\stuff\visual studio 2015\projects\test\test\a.h(9): error C3646: 'b': unknown override specifier
1>d:\stuff\visual studio 2015\projects\test\test\a.h(9): error C4430: missing type specifier - int assumed. Note: C++ does not support default-int
1> A.cpp
1>d:\stuff\visual studio 2015\projects\test\test\b.h(7): error C2061: syntax error: identifier 'A'
1>d:\stuff\visual studio 2015\projects\test\test\a.cpp(5): error C2660: 'B::bar': function does not take 1 arguments
1> Generating Code...
========== Build: 0 succeeded, 1 failed, 0 up-to-date, 0 skipped ==========
The code works fine if I don't include A.h in B.h and I don't pass anything to the bar function.
I tried to google what could cause these errors but I wasn't able to resolve the issue by myself becuase I don't understand what's causing these errors. What am I doing wrong?
In the header file of A.h, you have:
#include "B.h"
In the header file of B.h, you have:
#include "A.h"
You have a circular include where the definition of A
and B
depends on each other.
A simple solution is to use forward declaration in definition of class B
:
#include "A.h"
in file B.h with class B;
#include "A.h"
at the beginning of B.cpp
Notice however, you cannot use forward declaration for class A
, the reason is that you have a value b
of class B
as a member variable of class A
:
#pragma once
#include "B.h"
class A
{
public:
A();
void foo();
void sayHello();
B b; /// I mean this line here more specifically
};
The compiler need to know the the definition of class B
in order to determine the correct size for class A. That's why you have to put the #include "B.h"
at the very beginning of A.h.
Let's take a look at B.cpp and watch what happens with the includes:
#include "B.h"
B::B(){}
void B::bar(A *a) {
a->sayHello();
}
With B.h pasted in place of the include we get:
#include "A.h"
class B
{
public:
B();
void bar(A *a);
};
B::B(){}
void B::bar(A *a) {
a->sayHello();
}
And then placing A.h in place of its include:
#include "B.h"
class A
{
public:
A();
void foo();
void sayHello();
B b;
};
class B
{
public:
B();
void bar(A *a);
};
B::B(){}
void B::bar(A *a) {
a->sayHello();
}
And here we stop because pragma once
prevents re-inclusion of B.h
We can see that in class A
's definition we have B b;
, but class B
is not yet defined. Kaboom. A
cannot be defined without B
and B
is not defined yet.
A
must have the size of B
to satisfy B b;
, so it requires the full definition of B
. B
only needs to know A
exists because it only needs a pointer to A
to satisfy void bar(A *a);
. So...
A.h
#pragma once
#include "B.h"
class A
{
public:
A();
void foo();
void sayHello();
B b;
};
A.cpp
#include "A.h"
#include <iostream>
A::A() {}
void A::foo() {
b.bar(this);
}
void A::sayHello() {
std::cout << "Hello" << std::endl;
}
B.h
#pragma once
class A; // forward definition of class A
class B
{
public:
B();
void bar(A *a);
};
B.cpp
#include "A.h"
B::B(){}
void B::bar(A *a) {
a->sayHello();
}
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