Доступ к членам подкласса из указателя суперкласса C++

У меня есть массив пользовательских объектов класса Student. И CourseStudent, и ResearchStudent наследуются от Student, и все экземпляры Student являются одним из них.

У меня есть функция для просмотра массива, определения подтипа каждого студента, а затем вызова для них функций-членов, специфичных для подтипа.

Проблема в том, что из-за того, что эти функции не перегружены, их нет в Student, поэтому компилятор поднимает шум.

Если у меня есть указатель на Student, есть ли способ получить указатель на подтип этого Student? Нужно ли мне делать здесь какое-то поддельное приведение, чтобы обойти ошибку времени компиляции?


person David Mason    schedule 01.04.2010    source источник


Ответы (5)


Вам нужен динамический бросок:

Student * s = new ...;    // Create student of some sort.

if ( ResearchStudent * r = dynamic_cast<ReasearchStudent*>( s ) ) {
   r->ResFunc();
}
else if ( CourseStudent * c = dynamic_cast<CourseStudent*>( s ) ) {
   c->CourseFunc();
}
else {
   throw "Unknown student type.";
}

Обратите внимание, что при этом используется информация о типе, поддерживаемая компилятором, при условии, что класс имеет хотя бы одну виртуальную функцию - если ничего не помогает, сделайте деструктор виртуальным (как и должно быть в этом случае). Вы всегда должны предпочесть этот подход сохранению информации о вашем собственном типе.

person Community    schedule 01.04.2010

Лучше всего было бы использовать виртуальные функции:

class Student
{
   // ...
   virtual void SpecificFunction() = 0; /* = 0 means it's abstract; it must be implemented by a subclass */
   // ...
};

class CourseStudent
{
    void SpecificFunction() { ... }
};

Затем вы можете сделать:

Student *student;
student->SpecificFunction();

Альтернативой (хуже) может быть использование dynamic_cast:

Student *student;
CourseStudent *cs = dynamic_cast<CourseStudent *>(student);

if (cs) {
   /* student is a CourseStudent.. */
   cs->SpecificFunction();
}
person Thomas Bonini    schedule 01.04.2010
comment
При прочих равных условиях с точки зрения Java/C# динамическое приведение будет лучше, потому что таким образом ваш указатель будет типизирован так, как вы намереваетесь его использовать, вместо того, чтобы быть Student, для которого вызов SpecificFunction() дает возвращаемое значение CourseStudent . Но я уверен, что не все вещи равны — так что плохого в использовании динамического приведения? - person ACK_stoverflow; 21.03.2012

Виртуальные функции здесь неуместны, потому что функции-члены подкласса специфичны для этих подклассов (например, у CourseStudent есть список модулей, а у ResearchStudent нет, поэтому реализация функции getUnits() в ResearchStudent вообще не имеет смысла)

Я немного ознакомился с динамическими и статическими приведениями типов ( cplusplus.com typecasting ), и в этом случае я думаю, что статическое приведение более подходит.

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

Там, где есть вероятность того, что член не будет ожидаемого типа, статическое приведение не будет подходящим, поэтому я выберу динамическое приведение (это назначение, поэтому после его отправки код не нужно будет поддерживать, поэтому есть нет риска, что кто-то испортит это позже).

person David Mason    schedule 01.04.2010
comment
Я знаю, что это было давно, но я хотел сказать спасибо; Сегодня я столкнулся с очень похожей проблемой, и ваш ответ дал мне именно то решение, которое мне было нужно! - person GarrickW; 18.02.2012
comment
@GarrickW Рад слышать, что это все еще полезно - стоит усилий по поиску решения для идеи. - person David Mason; 23.02.2012

Это почти наверняка случай использования чистой виртуальной функции-члена в базовом классе с последующим переопределением в производных классах, где вы выполняете реальную работу.

person Marcelo Cantos    schedule 01.04.2010

Вам нужно static_cast для этого. Поскольку эти функции не являются виртуальными членами базового класса, вы не можете вызывать их через указатель на базовый класс. Вам нужно явно указать фактический тип объекта.

Эта проблема обычно лучше всего решается с помощью виртуальных функций - вам больше не понадобится проверка типа объекта в вашем коде, у вас будет меньше кода и меньше места для ошибок.

person sharptooth    schedule 01.04.2010