My FeedDiscussionsHeadless CMS
New
Sign in
Log inSign up
Learn more about Hashnode Headless CMSHashnode Headless CMS
Collaborate seamlessly with Hashnode Headless CMS for Enterprise.
Upgrade ✨Learn more

Circular dependency | Forward Declaration | #pragma once | C++

PRASHANTH H N's photo
PRASHANTH H N
·Apr 10, 2019

Are you wondering what circular dependency is? Are you trying to imagine the cases that would make the modules/classes circularly dependent on one another? Want to know the problems that we may get into while implementing circularly dependent modules? Want to know how Forward declaration / #pragma once solve those problems? You are at the right place.


So, What's circular dependency ?

When two or more modules are related to each other either directly or indirectly then they are said to be circularly dependent on each other.

Didn't get the point ? Let's take up an example to understand it better. Assume that we are writing an application for Flight booking following OOP way. Following are the two classes that we are going to implement as a part of it.

  • Class Flight : This will have the info of flight such as name, timings, service provider (like Indigo / Air India / etc. )

  • Class Service : This will have the info of service provider such as its places of service, flights it offers, etc.

What we are interested in here is that Class Flight should have a pointer to its Service (service provider) and Class Service should have pointers to all of its Flights.

Now, we can call Fight and Service as circularly dependent classes.


For the sake of simplicity, let's consider 2 classes A and B which are circularly dependent.

  • Class A : Has a private member string name, pointer to an object of class B and methods set, get and print.

  • Class B : Has a private member string name, pointer to an object of class A and methods set, get and print.

The following code will implement the same.

#include<iostream>
using namespace std;

class A{
    string name;
    B* b;
public:
    A();
    void set(B* pB);
    string get();
    void print();
};

class B{
    string name;
    A* a;
public:
    B();
    void set(A* pA);
    string get();
    void print();
};

A::A(){
    name = "Class A";
}

void A::set(B* pB){
    b = pB;
}

void A::print(){
    cout << name.c_str() << endl;
    cout << b->get().c_str() << endl;
}

string A::get(){
    return name;
}

B::B(){
    name = "Class B";
    a = new A();
}

void B::set(A* pA){
    a = pA;
}

void B::print(){
    cout << name.c_str() << endl;
    cout << a->get().c_str() << endl;
}

string B::get(){
    return name;
}

int main()
{
    A* pA = new A();
    B* pB = new B();

    pA->set(pB);
    pB->set(pA);

    cout << "Calling A's print " << endl;
    pA->print();

    cout << "Calling B's print " << endl;
    pB->print();

    return 0;
}

But, the fact is that the above code will not even get compiled. It will give a compile time error like below.

error: ‘B’ does not name a type
  B* b;
  ^

This is because, as the code is being compiled, when the compiler reads the line "B* b", it won't be having any known type / class by identifier "B".

So in order to avoid this problem, we have to declare "B" as a class before "A" This is called as forward declaration of class B . Like this,

#include<iostream>
using namespace std;

class B; //   Forward declaration of class B

class A{
    string name;
    B* b;
public:
    A();
    void set(B* pB);
    string get();
    void print();
};

Just to give a quick in-between summary, so far we have understood circular dependency and forward declaration. Oh ! the title of this article has a string #pragma once which is untouched yet.


Is there a question running in your mind that how is #pragma once related to circular dependency ? Let's get back to the application that we were discussing earlier, Flight Booking. The cpp file looks messy with the way we have implemented class A and B . Isn't it ? If we go on to implement our flight booking application in the same way( everything in one cpp file), it's going to be a big mess.

So the approach we take is to have separate header files for declaring the classes and their members and separate cpp files for implementing methods of each classes.

main.h

#include<iostream>
#include<vector>
using namespace std;

Service.h

#include "main.h"
#include "Flight.h"

class Flight;

class Service
{
    string m_strName;
    vector<Flight*> m_Flights;
public:
    Service();
    Service(string strName);
    void AddFlight(Flight* pFlight);
    void RemoveFlight(Flight* pFlight);
    string GetName();
    void PrintDetails();
};

Flight.h

#include "main.h"
#include "Service.h"

class Service;

class Flight
{
    string m_strName;
    Service* m_pService;
public:
    Flight();
    Flight(string strName);
    void SetService(Service* pService);
    void SetName(string strName);
    string GetName();
    void PrintDetails();
};

Implementations of Flight and Service classes and application can be found here => Flight.cpp , Service.cpp and main.cpp (application).

But this code has got a problem. It will not get compiled. Following is the compile time error we get.

Service.h(3): fatal error C1014: too many include files : depth = 1024

Flight.h(3): fatal error C1014: too many include files : depth = 1024

This is because compiler will be stuck in a deadlock between Service.h and Flight.h as they include each other. As the compiler parses Service.h, it will read [#include "Flight.h"] and tries to include Flight.h. But Flight.h has the statement [#include "Service.h"] which tells the compiler to include Service.h and hence the compiler will be stuck in this process.

So, how to solve this problem ? Are you thinking if there was an option that an included file should not be included again in a single compilation so that it would have solved the problem ? Yes, you are right. there is such a way. Including #pragma once at the beginning of these header files is one of the ways to solve this problem. There is also an alternative for this. That can be read here .


Complete source code (with Visual studio solution) can be found here .