Виртуальные функции здесь неуместны, потому что функции-члены подкласса являются специфическими для этих подклассов (например, CourseStudent имеет список модулей, тогда как ResearchStudent нет, поэтому реализация функции getUnits () в ResearchStudent вообще не будет иметь смысла )
Я немного читал о динамических и статических приведениях ( cplusplus.com typecasting ), и в этом случае я думаю, что статическое приведение более уместно.
Общим недостатком static_cast является то, что он не выполняет никакой проверки во время выполнения, чтобы убедиться, что объект, приведенный к подтипу, фактически является этим подтипом, а не каким-то другим. В этом случае я специально проверяю тип перед тем, как выполнить тип (используя закрытый член данных, который установлен в конструкторе подкласса и не имеет мутатора), поэтому, пока моя проверка хороша, не должно быть проблем со статическим приведением , Статическое приведение более эффективно, так как динамическое приведение требует больше ресурсов времени выполнения для проверки типов.
Там, где есть вероятность того, что элемент не является ожидаемым типом, статическое приведение не будет подходящим, поэтому я бы пошел на динамическое приведение (это назначение, поэтому после отправки кода не нужно будет поддерживать код так что нет риска, что кто-то испортит это позже).