Язык программирования Си++ (1119468), страница 8
Текст из файла (страница 8)
*/ };class B {int a;void b ();void h (char);public: void f (int); void h ();void h (int); int g;/* ... */ };class C: public A, public B { /* ... */ };void gg (C * pc){pc -> A::a = 1;pc -> A::b ();pc -> A::f ();pc -> B::f (1);pc -> A::g (); pc -> B::g = 1;pc -> h (‘a’);// ОШИБКА: проверка доступности h(char)pc -> h (1); } // нет ошибок: однозначно и доступноpc -> h ();159Чистые виртуальные функции• При программировании могут возникать ситуации, когда вбазовых классах виртуальные функции не могут выполнятьникаких реальных действий, становясь “чистыми”виртуальными функциями, например:class shape { public: virtual double area () { return 0; } };class rectangle: public shape { /* ...
*/ private: double height, width; /*...*/public: double area () { return height *width; }};class circle:public shape { /* ... */ private: double radius;/*...*/public: double area () { return PI * radius * radius;}};• Заданные описания позволяют создать массив указателей набазовый (абстрактный) класс и в одном цикле подсчитатьсумму площадей разнородных геометрических фигур:shape * p [N]; int i; double total_area = 0.0;for (i = 0; i < N; i ++) total_area += p [i] -> area ();160Чистые виртуальные функции• Реально могут существовать только объекты производныхклассов, именно на них могут указывать элементы массивауказателей• Если будут введены дополнительные плоские фигуры иописаны их классы и методы вычисления площади, тоуказатели на них также можно внести в общий массив, и безизменения основной программы вычислить общую площадь• Если тело виртуальной функции (например, функции area ()) избазового класса вообще не используется в программе, функцияявляется “чистой” виртуальной функцией• Для таких функций используется специальный синтаксис:class shape { /* ...
*/ public: /* ... */ virtual double area () = 0; };161Абстрактные классы• Абстрактным называется класс, содержащий хотя бы однучистую виртуальную функцию• Чистая виртуальная функция – это функция вида:virtual <тип_результата><имя_функции> (<параметры>) = 0;• Абстрактный класс может использоваться только как база дляпостроения других классов• Объекты абстрактного класса создавать нельзя, а указатели наних заводить можно• Абстрактный класс может содержать неконстантные членыданные, описания конструкторов и деструкторов• Класс, производный от абстрактного класса, может остатьсяабстрактным, если в нём конкретизированы не все чистыевиртуальные функции базовых классов, поскольку чистыевиртуальные функции наследуются и остаются виртуальными162Реализация виртуальныхфункций• Для реализации аппарата виртуальных методов используетсямеханизм косвенного вызова через специальные связанные сполиморфным типом объекта массивы указателей на функциичлены, такие массивы называются таблицами виртуальныхметодов (ТВМ)• Таблица виртуальных методов создаётся в одном экземпляредля каждого класса, в каждый полиморфный объекткомпилятор неявно помещает указатель tvm* ptvm насоответствующую таблицу ТВМ, в которой хранятся адресавиртуальных методов (число ссылок на ТВМ соответствуетчислу созданных объектов)163Реализация виртуальныхфункций• Пусть есть иерархия классов: A, B, Cclass A { int a;public: virtual void f (int);virtual void g (int);virtual void h (int);};class B: public A { int b;public: void g (int);};class C: public B { int c;public: void h (int);};A_TVM:&A::f&A::g&A::hB_TVM:&A::f&B::g&A::hC_TVM:&A::f&B::g&C::h164Таблица виртуальных функций• Пусть есть иерархия классов: A, B, C• Память для таблиц виртуальных функцийавтоматически отводится компилятором встатической области• Ссылки на одноимённые функции втаблицах виртуальных методов всехклассов одной наследственной иерархиивсегда находятся на одном и том же местеA_TVM:&A::f&A::g&A::hB_TVM:&A::f&B::g&A::hC_TVM:&A::f&B::g&C::h165Реализациявиртуальных функцийpaint aint bint c• Виртуальная функция g () может быть вызвана так:C x;A * pa = & x;pa -> g (‘a’);• Активный объект относится к типу C, но реализация методаполностью наследуется из класса B• При работе функции B::g () указатель this имеет доступ ковсем полям класса B и к открытым полям класса A (this ≡ pa):(* (pa -> c_tvm [index (g)])) (pa, ‘a’);• Издержки по памяти для каждого полиморфного объектавыливаются в неявное хранение дополнительного указателя166Реализация виртуальныхфункций• Сложнее реализовать виртуальные функции примножественном наследовании:class Aclass B{ public: virtual{ public: virtualvirtualclass C: public A, public B { public:void f (); };void f ();void g (); };void f (); }C x, * pc = & x;A * pa = & x;B * pb = & x;pb -> g (1);167Реализациявиртуальных функцийdelta (B)pbint aint bint cthis• Активный объект относится к типу C, но реализация методаполностью наследуется из класса B• При работе функции g () указатель this имеет доступ только ктой части активного объекта x типа C, которая унаследована откласса B (но не от A):this(* (pb -> c_tvm [index (g)])) ((B *) ((void *) (pb) + delta (B)), 1);• Смещение delta (B) определяет место в объекте типа C, начинаяс которого размещаются поля, унаследованные от класса B168Реализация виртуальныхфункций• В таблице виртуальных методов класса A размещается одназапись, так как в этом классе имеется только одна виртуальнаяфункция f ()• В таблице виртуальных методов класса B размещаются двезаписи: в этом классе определены сразу две виртуальныефункции – f () и g ()• В классе C имеются две функции функция g () унаследована от класса B функция f () есть переопределение виртуальных функций f (),имеющихся в базовых классах A и B169Таблица виртуальных функций• При множественном наследовании в строке таблицывиртуальных методов находятся сразу два значения: адрес телафункции и смещение начала доступной части данных объекта вобласти памяти этого объектаdelta (B)thisдля вызова B::g ()tvmint a(часть А)tvmint b(часть В)tvmint c(часть С)tvm для A и C (delta (A) = 0)& C::f0tvm для B и C& C::f- delta (B)& B::g- delta (B)ссылкисмещения• Строка таблицы имеет структуру:struct tvm_entry { void * (* fct) (); int delta; };170Реализация виртуальныхфункций• Значения смещений известны во время компиляции, онивычисляются непосредственно компилятором• Во время компиляции неизвестно, какое именно из заранеерассчитанных смещений надо использовать (указатель pbссылается на часть объекта C, унаследованную от класса B)• Во время исполнения вызовов pa -> f () или pb -> f ()формируется доступ к нужной функции f (), например:tvm_entry * vt = & pb -> tvm [index (f)];(* vt -> fct) ((B *) ((void*) (pb) + vt -> delta));171Пространства именования• Программные элементы (классы, объекты, функции),относящиеся к обработке связанной информацииможно объединять в единое пространство имён:namespace Student { class studentclass student1с: public studentclass student2c: public studentclass student3с: public studentclass student4с: public studentclass student5с: public student}Student :: student2c :: print () { ...
}{ ... };{ ... };{ ... };{ ... };{ ... };{ ... };172Пространства именования• Объявления функций можно объединять впространствах имён, создавая описания интерфейсов:namespace Trigon{ double acos (double x);double asin (double x);double atan (double x);double atan2 (double y, double x);double cos (double x);double sin (double x);double tan (double x);}173Пространства именования• Отдельно даются определения функций,описывающие реализацию функций:double Trigon :: acos (double x) { ... }double Trigon :: asin (double x) { ...
}double Trigon :: atan (double x) { ... }double Trigon :: atan2 (double y, double x) { ... }double Trigon :: cos (double x) { ... }double Trigon :: sin (double x) { ... }double Trigon :: tan (double x) { ... }174Пространства именования• Новый член пространства имён нельзя объявить внеопределения этого пространства имён:double Trigon :: ctg (double x);// ОШИБКА!• Такой подход позволяет обнаруживать ошибки настадии компиляции программы:double Trigon :: cas (double x);// ОШИБКА! Нет функцииdouble Trigon :: sin (float x);// ОШИБКА! Неверный тип175Пространства именования• Пространства имён есть область видимости• Обычные локальные и глобальные областивидимости, а также классы являютсяпространствами имён• Все имена могут относиться к некоторомупространству имён, единственное исключение– функция main ()• Пространства имён могут вкладыватьсядруг в друга176Пространства именования• Имена из других пространств именования следуетдополнительно специфицировать именами этих пространств:double Trigon :: tan (double x){ double c = cos (x);if (c) return sin (x) / c;return Error :: error;}• Объявление используемого пространства именования:double Trigon :: tan (double x){ using Error :: error;double c = cos (x);if (c) return sin (x) / c;return error;}177Пространства именования• Объявления об использовании некоторых элементовиз других пространств именования вводят локальныесинонимы для имён этих элементов, их можнососредоточить непосредственно в определениисобственного пространства имён:namespace Trigon{ double acos (double x); /* ...
*/double tan (double x);using Error::error;}178Пространства именования• Одно пространство имён можно сделать полностьюдоступным из другого:namespace Trigon{ double acos (double x); /* ... */double tan (double x);using namespace Error; // таких директив может быть много}• Директива using выполняет композициюпространств имён• Одинаковые имена в разных пространствахименования снабжаются спецификаторами и неприводят к конфликтам179Пространства именования• В каждой единице трансляции может быть однонеименованное пространство именования, в разных единицахтрансляции неименованные пространства считаются разными:namespace { double my_cos (double x); /* ...
*/ }• Имена, введённые в неименованных пространствах, доступныв объемлющих их областях видимости:namespace Уникальное_имя { double my_cos (double x); ... }using namespace Уникальное_имя;• Глобальные объекты могут стать доступными из вложенныхпространств именования при помощи спецификатора безимени::: p ++; // обращение к глобальной переменной р180Пространства именования• Псевдонимы пространств именования:namespace MSU = Moscow_State_University;/* ...
*/MSU :: double count_avb (const & student);• Пространства имён открыты: в любом местепрограммы можно продолжить определениеранее определённого пространстваименования: namespace Student { ... }#include <list>namespace Student { ... }181Пространства именования• Пространства именования не создают границ,препятствующих работе механизма перегрузкифункций:namespace A { void f (int);/* ... */ }namespace B { void f (char); /* ... */ }namespace C {using namespace A;using namespace B;void g (){ f (‘a’);// вызовется f (char)/* ...