Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

C++: passing the this pointer to another class

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?

like image 851
mrlux Avatar asked Mar 31 '16 23:03

mrlux


Video Answer


2 Answers

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:

  1. Replace the line #include "A.h" in file B.h with class B;
  2. Add #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.

like image 91
Yuchen Avatar answered Sep 24 '22 15:09

Yuchen


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();
}
like image 43
user4581301 Avatar answered Sep 22 '22 15:09

user4581301