cout << title << "\n";
}
};
int main()
{
B_class *p;
B_class B_ob;
D_class *dp;
D_class D_ob;
p = &B_ob; // адрес объекта базового класса
// Доступ к классу B_class через указатель.
p->put_author("Эмиль Золя");
// Доступ к классу D_class через "базовый" указатель.
р = &D_ob;
p->put_author("Уильям Шекспир");
// Покажем, что каждый автор относится к соответствующему объекту.
B_ob.show_author();
D_ob.show_author();
cout << "\n";
/* Поскольку функции put_title() и show_title() не являются частью базового класса, они недоступны через "базовый" указатель р, и поэтому к ним нужно обращаться либо непосредственно, либо, как показано здесь, через указатель на производный тип.
*/
dp = &D_ob;
dp->put_title("Буря");
p->show_author(); // Здесь можно использовать либо указатель р, либо указатель dp.
dp->show_title();
return 0;
}
При выполнении эта программа отображает следующие результаты.
Эмиль Золя
Уильям Шекспир
Уильям Шекспир
Название: Буря
В этом примере указатель р определяется как указатель на класс B_class. Но он может также ссылаться на объект производного класса D_class, причем его можно использовать для доступа только к тем элементам производного класса, которые унаследованы от базового. Однако следует помнить, что через "базовый" указатель невозможно получить доступ к тем членам, которые специфичны для производного класса. Вот почему к функции show_title() обращение реализуется с помощью указателя dp, который является указателем на производный класс.
Если вам нужно с помощью указателя на базовый класс получить доступ к элементам, определенным производным классом, необходимо привести этот указатель к типу указателя на производный тип. Например, при выполнении этой строки кода действительно будет вызвана функция show_title() объекта D_ob:
((D_class *)р)->show_title();
Внешний набор круглых скобок используется для связи операции приведения типа с указателем р, а не с типом, возвращаемым функцией show_title(). Несмотря на то что в использовании такой операции формально нет ничего некорректного, этого по возможности следует избегать, поскольку подобные приемы попросту вносят в код программы путаницу. (На самом деле большинство С++-программистов считают такой стиль программирования неудачным.)
Кроме того, необходимо понимать, что хотя "базовый" указатель можно использовать для доступа к объектам любого производного типа, обратное утверждение неверно. Другими словами, используя указатель на производный класс, нельзя получить доступ к объекту базового типа.
Указатель инкрементируется и декрементируется относительно своего базового типа. Следовательно, если указатель на базовый класс используется для доступа к объекту производного типа, инкрементирование или декрементирование не заставит его ссылаться на следующий объект производного класса. Вместо этого он будет указывать на следующий объект базового класса. Таким образом, инкрементирование или декрементирование указателя на базовый класс следует расценивать как некорректную операцию, если этот указатель используется для ссылки на объект производного класса.
Тот факт, что указатель на базовый тип можно использовать для ссылки на любой объект, выведенный из базового, чрезвычайно важен и принципиален для C++. Как будет показано ниже, эта гибкость является ключевым моментом для способа реализации динамического полиморфизма в C++.
Подобно указателям, ссылку на базовый класс также можно использовать для доступа к объекту производного типа. Эта возможность особенно часто применяется при передаче аргументов функциям. Параметр, который имеет тип ссылки на базовый класс, может принимать объекты базового класса, а также объекты любого другого типа, выведенного из него.