русс | укр

Языки программирования

ПаскальСиАссемблерJavaMatlabPhpHtmlJavaScriptCSSC#DelphiТурбо Пролог

Компьютерные сетиСистемное программное обеспечениеИнформационные технологииПрограммирование

Все о программировании


Linux Unix Алгоритмические языки Аналоговые и гибридные вычислительные устройства Архитектура микроконтроллеров Введение в разработку распределенных информационных систем Введение в численные методы Дискретная математика Информационное обслуживание пользователей Информация и моделирование в управлении производством Компьютерная графика Математическое и компьютерное моделирование Моделирование Нейрокомпьютеры Проектирование программ диагностики компьютерных систем и сетей Проектирование системных программ Системы счисления Теория статистики Теория оптимизации Уроки AutoCAD 3D Уроки базы данных Access Уроки Orcad Цифровые автоматы Шпаргалки по компьютеру Шпаргалки по программированию Экспертные системы Элементы теории информации

Раннее и позднее связывание


Дата добавления: 2013-12-23; просмотров: 2580; Нарушение авторских прав


Виртуальный базовый класс

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

#include <iostream.h>

class base { public: int i; };

class d1 : public base { public: int j };

class d2 : public base { public: int k; };

/* d3 наследует как d1, так и d2. Это означает, что в d3 имеется две копии base! */

class d3 : public d1, public d2

{

public:

int m;

};

int main()

{

d3 d;

d.i = 10; // неопределенность, какое i???

d.j = 20; d.k = 30; d.m = 40;

// также неопределенность, какое i???

cout << d.i << “ “ << d.j << " " << d.k << " " << d.m;

return 0;

}

Как показывает комментарий в данной программе, оба класса d1 и d2 наследуют класс base. Однако класс d3 наследует оба класса dl и d2. Это означает, что в классе d3 представлены две копии класса base. Поэтому в выражении типа

d.i = 20;

не ясно, какое именно i имеется в виду — относящееся к d1 или же относящееся к d2? Поскольку имеется две копии класса base в объекте d, то там имеются также две переменные d.i. Как видно, инструкция является двусмысленной в силу описанного наследования.

Имеется два способа исправить программу. Первый заключается в использовании оператора области видимости для переменной i с дальнейшим выбором вручную одного из i. Например, следующая версия программы компилируется и исполняется так, как это необходимо:



#include <iostream.h>

class base { public: int i; };

class d1 : public base { public: int j; };

class d2 : public base { public: int k; };

class d3 : public d1, public d2

{

public:

int m;

};

int main()

{

d3 d;

d.d2::i = 10; // область видимости определена, используется i для d2

d.j = 20; d.k = 30; d.m = 40;

// область видимости определена, используется i для d2

cout << d.d2::i << " “ << d.j << " " << d.k << “ “ << d.m;

return 0;

}

Как можно видеть, используя оператор области видимости ::, в программе вручную выбирается версия i класса d2. Тем не менее, данное решение порождает более глубокие вопросы: что если требуется только одна копия класса base? Имеется ли какой-либо способ предотвратить вклю­чение двух копий в класс d3? Как можно было догадаться, ответ на этот вопрос положительный. Решение достигается путем использования виртуального базового класса.

Когда два или более класса порождаются от одного общего базового класса, можно предот­вратить включение нескольких копий базового класса в объект-потомок этих классов путем объяв­ления базового класса виртуальным при его наследовании. Например, ниже приведена другая версия предыдущей программы, в которой d3 содержит только одну копию класса base:

#include <iostream.h>

class base { public: int i; };

class d1 : virtual public base { public: int j; };

class d2 : virtual public base ( public: int k; };

/* d3 наследует как d1 так и d2. Тем не менее в d3 имеется только одна копия base! */

class d3 : public d1, public d2 { public: int m; };

int main()

{

d3 d;

d.i = 10; // неопределенности больше нет

d.j = 20; d.k = 30; d.m = 40;

cout << d.i << " " << d.j << " " << d.k << " " << d.m;

return 0;

}

Как видно, ключевое слово virtual предшествует спецификации наследуемого класса. Теперь оба класса d1 и d2 наследуют класс base как виртуальный. Любое множественное наследование с их участием порождает теперь включение только одной копии класса base. Поэтому в классе d3 имеется только одна копия класса base, и, следовательно, d.i = 10 теперь не является двусмысленным выражением.

Необходимо иметь в виду еще одно обстоятельство: хотя оба класса d1 и d2 используют класс base как виртуальный, тем не менее, всякий объект класса d1 или d2 будет содержать в себе base. Например, следующий код абсолютно корректен:

// определение объекта класса d1

d1 myclass;

myclass.i = 100;

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

Имеются два термина, часто используемые, когда речь заходит об объектно-ориентированных языках программирования: раннее и позднее связывание. По отношению к C++ эти термины соответствуют событиям, которые возникают на этапе компиляции и на этапе исполнения про­граммы соответственно.

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

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

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



<== предыдущая лекция | следующая лекция ==>
Чисто виртуальные функции и абстрактные типы | Введение в обработку исключений


Карта сайта Карта сайта укр


Уроки php mysql Программирование

Онлайн система счисления Калькулятор онлайн обычный Инженерный калькулятор онлайн Замена русских букв на английские для вебмастеров Замена русских букв на английские

Аппаратное и программное обеспечение Графика и компьютерная сфера Интегрированная геоинформационная система Интернет Компьютер Комплектующие компьютера Лекции Методы и средства измерений неэлектрических величин Обслуживание компьютерных и периферийных устройств Операционные системы Параллельное программирование Проектирование электронных средств Периферийные устройства Полезные ресурсы для программистов Программы для программистов Статьи для программистов Cтруктура и организация данных


 


Не нашли то, что искали? Google вам в помощь!

 
 

© life-prog.ru При использовании материалов прямая ссылка на сайт обязательна.

Генерация страницы за: 0.008 сек.