Below code sample should answer your question in a rather modern C++ way.
#include <iostream>
#include <string>
#include <vector>
#include <memory>
// I have added a `std::string name` to the CPerson class and
// a `std::string subject` to the CTeachers class
// so both derived classes, CStudent and CTeacher have a name
// of the person involved and each derived class has
// something only it needs, MatrikulationNr for CStudent and
// Subject of teaching for CTeacher in order to deliver a more
// complete and more clearifying answer.
class CPerson {
int dummyMatriculationNr{ 0 };
std::string dummySubject{ "noTeacher" };
protected:
std::string name;
public:
std::string getName() { return name; }
virtual int& getMat() { return dummyMatriculationNr; }
virtual std::string getSubject() { return dummySubject; }
};
class CStudent : public CPerson {
int MatriculationNr{ 0 };
public:
CStudent() = delete; // we dont want anyone using this constructor
explicit CStudent(std::string name, int MatriculationNr) :
MatriculationNr{ MatriculationNr } {
this->name = name;
}
int& getMat() { return MatriculationNr; }
};
class CTeacher : public CPerson {
std::string subject{ "" }; // Subject of teaching
public:
CTeacher() = delete;
explicit CTeacher(std::string name, std::string subject) :
subject{ subject } {
this->name = name;
}
std::string getSubject() { return subject; }
};
int main() {
std::vector<std::unique_ptr<CPerson>> vp;
vp.push_back(std::make_unique<CStudent>("aStudentsName", 8989 ));// or emplace_back
vp.push_back(std::make_unique<CTeacher>("aTeachersName", "mathematics"));
for (auto& e : vp)
std::cout << "Name: " << e->getName() << " MatrNo: " << e->getMat()
<< " TeachingSubject: " << e->getSubject() << std::endl;
}
I hope above sample answers your question. However, using the keyword virtual
creates a virtual function table, often called vtable, at runtime which costs performance and is considered not to be high performance computing anymore.
Its also confusing to have a getMat()
function available in all derived classes when you need it only in one, the CStudents
derived class. Although meaningless in any other class, this function still returns some dummy value there. That can be irritating. Same for the getSubject()
function in CTeacher. See the output:
Name: aStudentsName MatrNo: 8989 TeachingSubject: noTeacher
Name: aTeachersName MatrNo: 0 TeachingSubject: mathematics
Consider to solve your question without any keyword virtual
and having getMat()
and int MatriculationNr
in CStudents
only and not in the base class at all. I know it is tempting to use virtual but its something to rather avoid as long as possible. For example, MFC, Microsoft Foundation Classes, the maybe biggest class inheritance project ever written, did not use virtual at all!
Consider the following code as an example:
#include <iostream>
#include <string>
#include <vector>
#include <variant>
// a code version without virtual
class CPerson {
protected:
std::string name;
public:
std::string getName() { return name; }
};
class CStudent : public CPerson {
int MatriculationNr{ 0 };
public:
CStudent() = delete; // we dont want anyone using this constructor
explicit CStudent(std::string name, int MatriculationNr) : MatriculationNr{ MatriculationNr } {
this->name = name;
}
int& getMat() { return MatriculationNr; }
};
class CTeacher : public CPerson {
std::string subject{ "" }; // Subject of teaching
public:
CTeacher() = delete;
explicit CTeacher(std::string name, std::string subject) : subject{ subject } {
this->name = name;
}
std::string getSubject() { return subject; }
};
int main() {
std::vector<CStudent> vs; // auto-deleted through RAII
std::vector<CTeacher> vt; // and good for serialisation and or database communication
vs.push_back(CStudent{ "aStudentsName", 9898 });
vt.push_back(CTeacher{ "aTeachersName", "mathematics" });
for (auto s : vs)
std::cout << s.getName() << " " << s.getMat() << std::endl;
for (auto t : vt)
std::cout << t.getName() << " " << t.getSubject() << std::endl << std::endl;
// and here we are done already,
// not listing the two different types in one vector
// but just using a vector for each derived class
//
// but lets try put them now into one vector
// using a more modern way through std::variant
// which keps all data in the vector and not only the
// CPerson part.
std::vector<std::variant<CStudent, CTeacher>> people;
// we could, for example, copy from above vectors
for (auto e : vs)
people.push_back(e);
for (auto e : vt)
people.push_back(e);
// we could insert new ones
people.push_back(CStudent { "aStudentsName1", 9899 });
people.push_back(CTeacher { "aTeachersName1", "physics" });
// and take that vector apart again
std::cout << std::endl << "Output from vector of variant:" << std::endl;
for (auto& e : people)
if (std::holds_alternative<CStudent>(e)) {
CStudent& s = std::get<CStudent>(e);
std::cout << s.getName() << " " << s.getMat() << std::endl;
}
else if (std::holds_alternative<CTeacher>(e)) {
CTeacher& s = std::get<CTeacher>(e);
std::cout << s.getName() << " " << s.getSubject() << std::endl;
}
}
There are numerous ways to achieve the goal to avoid virtual and I hope you did enjoy the above.