For your latest code, it might help if, instead of thinking of it as a "circular dependency", think of it as just "physically impossible".
Imagine if a Duck object contained a Goose object, and a Goose object contained a Duck object. What's inside a duck? A goose. What's inside that goose? A duck. Infinitely. It doesn't make physical sense.
The same thing is happening here. You have a Student containing a Class, but a Class contains a Student. It's not possible logically, regardless of what the C++ compiler says.
I might have more to say later but just wanted to say that before I left.
__________________________________________________________________
I'm going to assume the following setup:
A Classroom has a list of Students. [I'm using the term 'Classroom' just because it's confusing to talk about school classes vs C++ classes]
Each Student also has a pointer to the current class they belong to.
This is something how that would look like:
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
|
#include <iostream>
#include <vector>
#include "Classroom.h"
#include "Student.h"
using namespace std;
int main()
{
std::vector<Student> students {
{ "Alice", "11/21/2019" },
{ "Bob", "01/02/1993" },
{ "Charlie", "07/20/1969" }
};
Classroom classroom("History", students);
std::vector<Student>& students_in_classroom = classroom.getStudents();
Student& alice = students_in_classroom[0];
std::cout << alice.getClassroomName() << '\n';
return 0;
}
|
Classroom.h
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22
|
// Classroom.h
#ifndef CLASSROOM_H
#define CLASSROOM_H
#include <string>
#include <vector>
class Student;
class Classroom
{
public:
Classroom(std::string className, const std::vector<Student> students);
std::string getName();
std::vector<Student>& getStudents();
private:
std::string name;
std::vector<Student> students;
};
#endif // CLASSROOM_H
|
Classroom.cpp
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20
|
#include "Classroom.h"
#include "Student.h" // to be able to access members of student
Classroom::Classroom(std::string className, const std::vector<Student> students)
: name(className), students(students) {
// point each student back to the classroom
for (Student& student : this->students)
{
student.setClassroom(this);
}
}
std::string Classroom::getName() {
return name;
}
std::vector<Student>& Classroom::getStudents() {
return students;
}
|
Student.h
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
|
#ifndef STUDENT_H
#define STUDENT_H
#include <string>
class Classroom;
class Student
{
public:
Student(std::string name, std::string dateOfBirth);
std::string getName();
std::string getDOB();
Classroom* getClassroom();
std::string getClassroomName();
void setClassroom(Classroom* classroom);
private:
std::string name;
std::string dateOfBirth;
Classroom* classroom;
};
#endif
|
Student.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
|
#include "Student.h"
#include "Classroom.h"
Student::Student(std::string name,std::string dateOfBirth)
: name(name), dateOfBirth(dateOfBirth), classroom(nullptr) { }
std::string Student::getName() {
return name;
}
std::string Student::getDOB() {
return dateOfBirth;
}
Classroom* Student::getClassroom() {
return classroom;
};
void Student::setClassroom(Classroom* classroom)
{
this->classroom = classroom;
}
// Just an example to show the need to dereference the Classroom pointer
std::string Student::getClassroomName()
{
if (classroom != nullptr)
return classroom->getName();
else
return "<none>";
}
|
g++ classroom.cpp student.cpp main.cpp -o main
Output:
This breaks any would-be circular dependency.
A classroom contains a list of Students, but Students only have a pointer back to which classroom they belong to.
When declaring a pointer or a container of objects, you don't need to know the member variables/functions of a class, so you can get away with a forward declaration.
But to actually use the member variable, e.g. (my_student.my_object++;) then you need to have the full class definition known.