Добавил:
Upload Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:
samples / Zaochniki / ООП.doc
Скачиваний:
28
Добавлен:
25.03.2015
Размер:
183.81 Кб
Скачать

№7 Преобразование типов в производных классах

В C++ классы, как правило, не разрознены, а иерархически связаны друг с другом. Вследствие этого, иногда приходится выполнять какие-то преобразования между классами, находящимися на разных уровнях дерева. Рас­смотрим следующих два класса:

class А { };

class В: public A {};

Поскольку В порожден от А, наследуемые компоненты А доступны в В. Следовательно, В - это что-то вроде над­множества А. Таким образом, возможно преобразование объекта В в А, но не наоборот.

Неявное преобразование, когда необходимо, осуществляется и при вызове функций, как показано в листинге 1.

Листинг 1. Неявное преобразование типов при вызове функций

void foo(A* object) {}

void main ()

{A* ap = new A;

B* bp = new B;

Foo (ap);

foo (bp) ;}

Поддержка преобразования классов является замечательной возможностью C++. Она позволяет компилятору во время компиляции эффективно разделять и отслеживать объекты. Расширение подобного качества превращает­ся в полиморфизм, предоставляющий возможность объектам вести себя полностью независимо, т.е. способом, определяемым во время выполнения.

Порождая один класс от другого, можно легко прийти к такой ситуации, когда в нескольких классах использу­ются переменные и функции с одинаковыми именами. Как сообщить компилятору, с какой именно копией пере­менной или функции работать? При поиске нужной функции компилятор просматривает дерево наследования начиная с класса В и использует "ближайшую" к классу В функцию. Если имя в базовом классе переобъявляется в производном классе, то имя в производном классе подавляет соответствующее имя в базовом классе.

В C++ можно принудить компилятор "видеть" за пределами текущей области видимости и обеспечить доступ к именам, которые в противном случае были бы невидимыми. Это делается с помощью опера­тора разрешения видимости, а процесс, естественно, называется разрешением видимости. Общая форма оператора разрешения видимости такова:

<имя класса> : : <идентификатор из класса>

где <имя класса> — это имя базового или производного класса, а <идентификатор из класса> — это имя любой переменной или функции, объявленной в классе.

Для того чтобы использовать оператор разрешения видимости, модифицируем класс В следующим образом:

class В: public A {

public:

int foo ( ) { return 2; }

int f ( ) { return A::foo( ); }

};

Теперь вызов функции В : : f ( ) приведет к вызову функции foo ( ) класса А. Оператор разрешения видимости используется не только внутри функций класса, но и при вызове функции.

Если есть несколько уровней наследования, то оператор разрешения видимости предоставля­ет доступ к элементам любого базового класса с учетом, разумеется, привилегий доступа. Заметьте, что разрешение видимости нужно только тогда, когда производный класс использует такой же идентификатор, как и в базовом классе. Рассмотрим листинг 2.

Листинг 2. Использование разрешения видимости после оператора . (точка)

class A { public:

int value;

};

class В: public A {

public:

int count;

};

void main( )

{

В b;

int i = b.count;

int j = b.B : : count; // излишне, но правильно

int k = b.value;

int l = b.A : : value; // тоже излишне, и тоже правильно

}

Представленный фрагмент кода показывает необычный синтаксис, используемый с оператором разрешения ви­димости. Подобная запись применяется с указателями на объект и ссылками. Оператор разрешения видимости не нужен, если идентификатор встречается в дереве наследования только один раз или находится в данной области видимости.

Соседние файлы в папке Zaochniki