How does one enforce a derived class to have member data of a specific derived type.
i.e.,
class Project {
public:
int projdata;
};
class Article: public Project {
};
class Building: public Project {
};
class Emplooyee {
public:
std::vector<Project> projs;
}
class Architect: public Employee {
};
class Writer: public Employee {
};
How do I now enforce that Architect objects only have projects of type Building, while Novelist only have projects of type Article? i.e., I want to have something like
class Architect: public Employee {
public:
std::vector<Building> projs;
};
and
class Novelist: public Employee {
public:
std::vector<Article> projs;
};
I could also store pointers to projects and then store cast them into the correct type. Is there a technique or design pattern to enforce such corresponding inheritance rules on members of derived classes?
A compile time solution is to make the base a template:
template<class Proj>
class Emplooyee {
public:
std::vector<Proj> projs;
}
class Architect: public Employee<Building> {};
class Writer: public Employee<Article> {};
Additionally, you can add a one additional non-template base so that Architect
and Writer
are part of same hierarchy, but that non-template base cannot deal with the projs
member.
If a template not an option, then you must rely on runtime checks. For that, Project
must be a polymorphic type, and you must use typeid
or dynamic_cast
, to enforce the invariant. And you must use indirection to store the Project
's in the first place. std::vector<Project>
cannot store any Building
nor Article
objects because it only stores Project
objects only
Like you mentioned, you could store polymorphic pointers in the base class:
class Employee {
public:
std::vector<Project*> projs;
}
And use dynamic_cast
to downcast them:
dynamic_cast<Building*>(projs[i])->doSomething();
But I wouldn't recommend this approach(unless necessary) since this will require you to manage the memory behind those pointers. (Which of course can be off-loaded to std::unique_ptr
for example.)
Unless you require the Employee to be a polymorphic class, far simpler approach would be to use a class template
template <typename T>
class Employee {
public:
std::vector<T> projs;
}
which can be used like so:
class Architect : public Employee<Building> {
};
Architect architect;
architect.projs.push_back(Building());