Compiler Cannot Recognize My Class in C++ - Cyclic Dependency

compiler cannot recognize my class in c++ - cyclic dependency

As i said in the comment, the problem is due to cyclic dependency. In particular, your

Student.hpp includes --> Grad.hpp which in turn includes --> Core.hpp which finally includes --> Student.hpp

So as you can see from above, you ended up where you started, namely at Student.hpp. This is why it is called cyclic dependency.

To solve this just remove the #include <c3/school/Student.hpp> from Core.hpp. This is because for the friend declaration friend class Student, you don't need to forward declare or include the Student class.

So the modified/correct Core.hpp file looks like this:

#ifndef C3_CORE_HPP
#define C3_CORE_HPP

#include <c3/utils/Str.hpp>
#include <c3/utils/Vec.hpp>
//note i have removed the include header from here

class Core {
//other members here as before

private:
Str name;
double midterm{}, final{};

friend class Student;//THIS WORKS WITHOUT INCLUDING Student.hpp
};

std::istream &read_hw(std::istream &in, Vec<double> &hws);

#endif //C3_CORE_HPP

Unable to resolve circular dependency

The error tells you that there is no reference to the whole Method.

In other words you need to define the Method in a *.cpp File or directly in the header. See for example this overview or this similar question. The answers tell you that is actual not a Compiler error but a Linker error.

Edit: As Hans Olsson Answer suggests, it could also be that you need to include your cpp files.

If you do not exactly know how the implementation will look like but want to know if you header-concept works you can implement them empty

virtual void doSomething (const MyClass&) const override {}

Or compiler your code with gcc with the flag "-c" which tells gcc enter to just do the compiling not the linking.

Need a solution for circular dependency in C++/CLI (compiler error)

You solve this the same way you would in ordinary C++: you first declare the type and its functions, then use them, and only define them after that, outside of the rest of the type's code. So, if you want to have everything in one file (and not deal with header files etc.), you could do something like:

public ref class EventHandler
{
public:
EventHandler(){}

void DataChanged(Object^ sender, DataTableEventArgs ^arg);
};

public ref class DTManager
{
// all of DTManager's code goes here; you can use EventHandler::DataChanged
};

void EventHandler::DataChanged(Object^ sender, DataTableEventArgs ^arg);
{
DTManager::DataChanged(arg);
}

The normal C++ way is to declare all types and their functions in header files and put only their definitions into code files. Doing it this way is much easier for big projects, especially if you have circular dependencies.

Resolve build errors due to circular dependency amongst classes

The way to think about this is to "think like a compiler".

Imagine you are writing a compiler. And you see code like this.

// file: A.h
class A {
B _b;
};

// file: B.h
class B {
A _a;
};

// file main.cc
#include "A.h"
#include "B.h"
int main(...) {
A a;
}

When you are compiling the .cc file (remember that the .cc and not the .h is the unit of compilation), you need to allocate space for object A. So, well, how much space then? Enough to store B! What's the size of B then? Enough to store A! Oops.

Clearly a circular reference that you must break.

You can break it by allowing the compiler to instead reserve as much space as it knows about upfront - pointers and references, for example, will always be 32 or 64 bits (depending on the architecture) and so if you replaced (either one) by a pointer or reference, things would be great. Let's say we replace in A:

// file: A.h
class A {
// both these are fine, so are various const versions of the same.
B& _b_ref;
B* _b_ptr;
};

Now things are better. Somewhat. main() still says:

// file: main.cc
#include "A.h" // <-- Houston, we have a problem

#include, for all extents and purposes (if you take the preprocessor out) just copies the file into the .cc. So really, the .cc looks like:

// file: partially_pre_processed_main.cc
class A {
B& _b_ref;
B* _b_ptr;
};
#include "B.h"
int main (...) {
A a;
}

You can see why the compiler can't deal with this - it has no idea what B is - it has never even seen the symbol before.

So let's tell the compiler about B. This is known as a forward declaration, and is discussed further in this answer.

// main.cc
class B;
#include "A.h"
#include "B.h"
int main (...) {
A a;
}

This works. It is not great. But at this point you should have an understanding of the circular reference problem and what we did to "fix" it, albeit the fix is bad.

The reason this fix is bad is because the next person to #include "A.h" will have to declare B before they can use it and will get a terrible #include error. So let's move the declaration into A.h itself.

// file: A.h
class B;
class A {
B* _b; // or any of the other variants.
};

And in B.h, at this point, you can just #include "A.h" directly.

// file: B.h
#include "A.h"
class B {
// note that this is cool because the compiler knows by this time
// how much space A will need.
A _a;
}

HTH.

C++ cyclic dependency of classes (Singleton)

If you can't use .cpp files for some reason, you can do this:

a.h:

#pragma once

class A {
public:
static A& getInstance();
int i;
int sum();

private:
A();
};

a_impl.h:

#pragma once
#include "a.h"
#include "b.h"

inline A& A::getInstance() {
static A instance;
return instance;
}

inline int A::sum() {
return i + B::getInstance().j;
}

inline A::A() {
}

b.h:

#pragma once

class B {
public:
static B& getInstance();
int j;
int sum();

private:
B();
};

b_impl.h:

#pragma once
#include "a.h"
#include "b.h"

inline B& B::getInstance() {
static B instance;
return instance;
}

inline int B::sum() {
return j + A::getInstance().i;
}

inline B::B() {
}

And then first include declarations a.h and b.h, and then implementations a_impl.h and b_impl.h:

#include "a.h"
#include "b.h"
#include "a_impl.h"
#include "b_impl.h"
#include <iostream>

int main() {
A::getInstance().i = 1;
B::getInstance().j = 2;
int t1 = A::getInstance().sum();
int t2 = B::getInstance().sum();
std::cout << t1 << std::endl;
std::cout << t2 << std::endl;
}

Now it will compile. In this particular example, B (or A) could've been implemented inside class definition (so, no b_impl.h). I separated declarations and definitions for both classes for the sake of symmetry.

How can I solve this circular dependency?

Since this is a has a relationship between DESEngine and UserEvents with in its class structure you can slightly redesign your classes that shouldn't lose any functionality. This slight change in design should improve on memory management and the relationships or bonds between classes. When a class Has a <T> or Owns an object to another class or structure the owning class only needs to include the objects header in its own header and shouldn't need a class prototype declaration. Now the class that belongs to another should not include the owning class's header within its own header but does need the class prototype declaration and needs to include the owning class'sheaderin itsimplementation` file.

Also as for the ownership relationship that is where smart pointers come into play. Since your DESEngine class object owns a UserEvents object it is safe to have a std::unique_ptr<T> in the owning class. This will create a pointer of T for you and should also help to manage that allocation and de-allocation of memory and resetting the pointers to avoid both memory leaks and dangling pointer issues. With this only this instance of DESEngine will be able to access and modify that instance or copy of UserEvents. Now if you need other resources to modify and have access to UserEvents besides the engine you can easily change that to a std::shared_ptr<T>.

Here is an example of a set of classes...

EXAMPLE

Owner.h

#ifndef OWNER_H
#define OWNER_H

#include "SomeObject.h" // This class is owned by Owner since Owner has a SomeObject class object

/*No Need To Have Class Prototype Declaration*/ // #include "SomeClass.h"
// This does however need to be included in "Owner.cpp"

class Owner {
std::unique_ptr<SomeObject> myRestrictedObject;
std::shared_ptr<SomeObject> mySharedResourceObject;
};
#endif // OWNER_H

Owner.cpp

#include "stdafx.h" // if used  
#include "SomeObject.h"
#include "SomeOtherClasses.h"

// Class Body Or Implementation Definitions

SomeObject.h

#ifndef SOME_OBJECT_H
#define SOME_OBJECT_H

// Since Owner.h already has #include `THIS.h` we do not want to include it here but we will need this:
class Owner;

class SomeObject {
private:
// Google Search For Using Friend relationship.
};

#endif // SOME_OBJECT_H

SomeObject.cpp

// We need to include Owner.h here
#include "Owner.h"

Now when working with SomeObject that is owned by another, you should be able to alleviate the need to contain a pointer or a reference to the owning object simply by making these two classes friends with each other either full friend class or by specific methods within each class. With this you can control the internal relationship of how the two class interact with other. With careful design the user of your product code shouldn't have to worry about the internal implementation details of your functions, class's and their methods, and templates if it is well structured and the functions do what they are meant to do and are named accordingly for readability.

Now if you want this object to contain a pointer of its owning object then you might want to step away from using friends but also try avoiding the need for either of them to accept a pointer or an object of each other. Another words both classes should have default constructors this way if one relies on another for construction it can always use the default constructor first then you can use an initialize or an update method to change the class's internal variable pointer to another class by that method. In some cases or needs you could have one class be a static class as opposed to an actual object. Then you would need a static get function to return its *this back to you as a static pointer then other depending classes could use the static pointer as well especially if you will only ever have 1 instance of that class object.

For using friend that can easily be googled to see how it is done. As for the later option I can give a small illustration if requested.

Since it appears that your application should only ever have a single instance of either DESEngine and UserEvents for there should be no need to have multiple instances of these unless if you are pooling a bunch of UserEvents objects then you could derive these two class from a base Singleton interface. Then make sure both of these objects declares a static pointer to itself as well a get method for that pointer. Then other classes that use this object can use that static pointer, or similarly but instead of being a static singleton you can use smart pointers or even a combination of both. It all depends on your particular needs.

The placement of your includes and your class prototype declarations are the most important deciding factor when it comes to circular dependencies. Now you are also showing other classes that these two classes use with other header files being included and it could be possible that one of those might be the culprit too but didn't show up until just now with these two classes.

I would also like to add on to WhozCraig's comment that you are passing a reference of DESEngine to a UserEvents constructor. So let's see what the compiler is trying to understand from you...

Somewhere in your source in some other function you are creating an instance of DESEngine such as;

main.cpp

#include "DESEngine.h"

int main() {
DESEngine engine; // This is okay because you declared it with a default constructor.

}

So the compiler goes into DESEngine.h and looks at the declaration of this class before the definitions and it says to itself okay I need a UserEvents class let's go to its constructor, Okay this construct requires a Reference to a DESEngine object and that is coming from DESEngine variable declaration to the current UserEvents and I haven't yet finished declaring or defining the DESEngine object. So it goes around the circle trying to complete the definitions based on the declarations.



Related Topics



Leave a reply



Submit