Student class with std::string
Consider replacing this:

Student(std::string name) {
    this-&gt;name = name;
}


with one of these two:

Student(std::string n)
: name{ std::move(n) } // assign here, not in function body
{
}

Student(const std::string&amp; n)
: name(n) // assign here, not in function body
{
}


They are more efficient and more idiomatic.

The first takes the argument by value (constructs a string copy when it receives an argument) , then moves the constructed value into Student::name.

The second is marginally faster than the first (but this speed difference should not matter in most use cases, so which you choose is a matter of style), but it doesn't express as well as the first, the idea that Student gets it's own copy of the string (passing by value means "the function gets it's own copy of the value", or "the function takes ownership of the argument").