
- •1.Объявление и определение класса.
- •3. Дружественные функции.
- •4. Пеpегpузка опеpаций ( синтаксис operator)
- •5. Статические компоненты класса.
- •7. Иерархия классов ( последовательность работы конструкторов и деструкторов )
- •8. Доступ к наследуемым компонентам
- •9. Виртуальные функции (когда применяются, форма вызова)
- •10 . Шаблоны ( пример template)
- •15 Преобразование типов данных.
- •17 Ввод-вывод в файл. Сохранение объектов в файле.
- •18 Обработка исключений (блоки try, throw, catch)
- •Примеры обработки исключительных ситуаций
- •20. Паттерны и их классификация. Принцип классификации паттернов проектирования
- •Паттерны проектирования классов/обьектов
- •21. Абстрактная фабрика.
- •22. Классификация типов данных. Система типов
- •Oбщий взгляд
- •Класс String
- •Объявление строк. Конструкторы класса string
- •Операции над строками
- •Цикл foreach
- •Наследование
- •Добавление полей потомком
- •Конструкторы родителей и потомков
- •Интерфейсы
- •Две стратегии реализации интерфейса
- •Преобразование к классу интерфейса
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С; ABC). Наследуя каждому родителю, производный класс может получить несколько копий одних и тех же полей, что приводит к неоднозначности обращения к этим полям. Добавление слова 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 здесь необязательно
};
Таким образом, механизм виртуальных функций заставляет функцию с одним и тем же именем работать по-разному в зависимости от того, объектом какого класса она вызывается. Вызов виртуальных функций обычно производится посредством указателей на базовый класс.
(Виртуальность является частью полиморфизма. Полиморфизм, наряду с инкапсуляцией и наследованием является, основой объектно-ориентированного программирования. Кроме виртуальности, к полиморфизму относятся перегруженные функции и операции).
Механизм виртуальных функций основан на позднем (динамическом) связывании. Раннее связывание характерно для языка С – код вызова функции является частью откомпилированного кода всей программы. При позднем связывании виртуальная функция не связывается с объектом при компиляции. Если в некотором классе имеется хотя бы одна виртуальная функция, то все объекты этого класса содержат указатель на связанную с их классом виртуальную таблицу указателей функций этого класса. Доступ к виртуальной функции осуществляется посредством косвенной адресации – через этот указатель и соответствующую таблицу. (Отметим, что виртуальная функция не может быть статическим компонентом класса).
Использование виртуальных функций снижает быстродействие и увеличивает размер объектов класса, содержащих такие функции. Но это – неизбежная плата за эффективность.