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

8. Доступ к наследуемым компонентам

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

Доступ в базовом классе

Модификатор доступа

Унаследованные права доступа

private

private

не доступен

protected

private

private

public

private

private

private

public

не доступен

protected

public

protected

public

public

public

Свойство protected аналогично свойству private, но доступ к такому компоненту возможен как через интерфейс базового класса, так и через интерфейс производного. По умолчанию для базового класса типа class в качестве модификатора доступа используется private, для класса struct – public.

Ниже следует программа, в которой класс Node задает тип данных, которым является элемент одностороннего списка; класс List является базовым, класс Stack – производным (List  Stack):

struct Node { int elem; Node *next; Node (int el, Node *ptr) {elem=el; next=ptr;} }; class List { protected: Node *head; public: List() {head=0;} List(int a) {head=new Node(a,0);} void Show(void); ~List(); }; class Stack: public List { public: Stack (int el): List(el){}; void Push(int); int Pop(); };

Унаследованная переменная head может быть использована в теле функций Push и Pop, но в программе она доступна только через интерфейс классов – функции Push, Pop, Show.

Отметим следующий момент: если производный класс был унаследован из базового с модификатором public, то указатель на базовый класс можно использовать и как указатель на производный класс без какого-либо преобразования типов:

указатель_на_базовый = указатель_на_производный;

Существуют виртуальные базовые классы. Необходимость таковых появляется при множественном наследовании в тех случаях, когда в разных родительских классах есть одинаковые поля (например, при следующей иерархии: AС; ABC). Наследуя каждому родителю, производный класс может получить несколько копий одних и тех же полей, что приводит к неоднозначности обращения к этим полям. Добавление слова virtual в списке базовых классов (перед модификатором доступа) устраняет дублирование.

9. Виртуальные функции (когда применяются, форма вызова)

Рассмотрим следующую программу.

class Parent{

public:

double F1(double x){ return x*x;}

double F2(double x { return F1(x)/2; }

};

class Child : public Parent {

public:

double F1( double x) { return x*x*x; } //Переопределение функции F1

};

void main(){

Child child;

cout<<child.F2(3)<<endl;

}

Здесь класс Child, производный от класса Parent, наследует функцию F2, но переопределяет функцию F1. Компилятор оттранслирует выражение child.F2(3) в обращение к унаследованной функции Parent::F2, которая в свою очередь вызовет Parent::F1, а не Child::F1. Полученный результат поэтому равен 4,5 вместо ожидаемого 13,5. Объявление функции F1 виртуальной приведет к правильному результату.

Свойство виртуальности задается ключевым словом virtual перед типом функции

class Parent { ... public: virtual double F1(double x){ return x*x; };

// В базовом слово virtual обязательно

... }; class Child :public Parent { ... public: virtual double F1( double x) { return x*x*x; }

// Повторение virtual здесь необязательно

};

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

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

Механизм виртуальных функций основан на позднем (динамическом) связывании. Раннее связывание характерно для языка С – код вызова функции является частью откомпилированного кода всей программы. При позднем связывании виртуальная функция не связывается с объектом при компиляции. Если в некотором классе имеется хотя бы одна виртуальная функция, то все объекты этого класса содержат указатель на связанную с их классом виртуальную таблицу указателей функций этого класса. Доступ к виртуальной функции осуществляется посредством косвенной адресации – через этот указатель и соответствующую таблицу. (Отметим, что виртуальная функция не может быть статическим компонентом класса).

Использование виртуальных функций снижает быстродействие и увеличивает размер объектов класса, содержащих такие функции. Но это – неизбежная плата за эффективность.

Соседние файлы в предмете [НЕСОРТИРОВАННОЕ]