Добавил:
Upload Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:
Лекции C++.doc
Скачиваний:
7
Добавлен:
01.05.2025
Размер:
1.44 Mб
Скачать

5.5 Указатели и ссылки на производные типы

Прежде чем переходить к виртуальным функциям и полиморфизму, следует объяснить один из их важнейших атрибутов. Начнем с указателей. В общем случае указатель одного типа не может указы­вать на объект другого типа. Из этого правила, однако, есть исключение, которое относится только к производным классам. В C++ указатель на базовый класс может указывать на объект производного класса, полученного из этого базового класса. Предположим, например, что имеется базовый класс B_class и его производный класс D_class. В C++ любой указатель типа B_class* может также указы­вать на объект типа D_class. Например, если имеются следующие объявления переменных:

B_class *p; // указатель на объект типа B_class

B_class B; // объект типа B_class

D_class D; // объект типа D_class

то следующие присваивания абсолютно законны:

р = &B; // р указывает на объект типа B_class

р = &D; /* р указывает на объект типа D_class, являющийся объектом, порожденным от B_class */

Используя указатель р, можно получить доступ ко всем членам D, которые наследованы от B. Однако специфические члены D не могут быть получены с использованием указателя р (по крайней мере до тех пор, пока не будет осуществлено приведение типов). Это является след­ствием того, что указатель «знает» только о членах базового типа и не знает ничего о специфи­ческих членах производных типов.

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

#include <iostream.h>

#include <string.h>

class CName

{

char name[80];

public:

void put_name(char *s) { strcpy(name, s); }

void show_name() { cout << name << " "; }

};

class CBook: public CName

{

char phone_num[80];

public:

void put_phone(char *num) { strcpy(phone_num, num); }

void show_phone() { cout << phone_num << "\n"; }

};

int main()

{

CName *p_name, name;

CBook *p_book, book;

p_name = &name;

// доступ к CName через указатель

p_name->put_name("Ivanov I. I.");

// доступ к CBook через указатель

p_name = &book;

p_name->put_name("Petrov P. P.");

// показать каждое имя соответствующего объекта

name.show_name();

book.showname();

cout << "\n";

/* поскольку put_phone и show_phone не являются частью базового класса, они не доступны через указатель на базовый класс и доступ должен осуществляться или напрямую, или, как показано ниже, через указатель на порожденный класс */

p_book = &book;

p_book->put_phone("555 555-1234");

p_name->show_name(); // в данной строке могут использоваться р и dp

p_book->show_phone();

return 0;

}

В этом примере указатель p_name определен как указатель на класс CName. Однако он может указы­вать также на объект производного класса CBook и может использоваться для доступа к членам производного класса, которые были определены в базовом классе. Вместе с тем следует запом­нить, что этот указатель не может использоваться для доступа к членам, специфическим для про­изводного класса, до тех пор, пока не выполнено приведение типов. Именно поэтому доступ к функции show_phone() осуществляется с использованием указателя p_book, являющегося указателем на производный класс.

Если необходимо получить доступ к элементам производного класса с помощью указателя, имеющего тип указателя на базовый класс, необходимо воспользоваться приведением типов. На­пример, в следующей строке кода осуществляется вызов функции show_phone() класса CBook:

((CBook*)p_name)->show_phone();

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

Хотя указатель на базовый класс может использоваться в качестве указателя на производный объект, обратное не имеет места. Это означает, что указатель на производный класс не может использоваться для доступа к объектам базо­вого типа. И еще одно замечание. Инкремент и декремент указателя выполняются по отношению к его базовому типу. Таким образом, если указатель на базовый класс указывает на объект произ­водного класса, инкремент или декремент его не даст указатель на следующий объект производ­ного класса. Поэтому нельзя использовать операции инкремента и декремента указателей, когда они указывают на объект производного класса.