Добавил:
Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:

книги / Технологии разработки объектно-ориентированных программ на язык C++. Основы объектно-ориентированного программирования на алгоритмическом языке C++

.pdf
Скачиваний:
7
Добавлен:
12.11.2023
Размер:
876.09 Кб
Скачать

Рис. 18.1. Пример множественного наследования

Множественное наследование может вызывать проблемы, если

вбазовых классах есть одинаковые поля!

18.7.Закрытое и защищенное наследование классов

ВC++ имеется еще одно средство реализации отношений классов – закрытое наследование. При использовании закрытого наследования открытые (public) и защищенные (protected) члены базового

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

При открытом наследовании открытые методы базового класса становятся открытыми методами производного класса, т.е. производный класс наследует интерфейс базового класса. А при закрытом наследовании открытые методы базового класса становятся закрытыми методами производного класса, т.е. производный класс не наследует интерфейс базового класса.

Защищенное наследование – это разновидность закрытого наследования. В этом случае при объявлении базового класса указывается ключевое слово protected. При защищенном наследовании открытые и защищенные члены базового класса становятся защищенными членами производного класса. Как и при закрытом наследовании, интерфейс базового класса доступен в производном клас-

31

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

18.8. Доступ к методам и объектам базового класса при закрытом наследовании

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

Допустим, имеется некий класс, который содержит объект класса string. Тогда при закрытом наследовании для обращения к методу size() объектатипаstring необходимоиспользоватьтакойсинтаксис:

string::size();

Доступ к объектам базового класса осуществляется с помощью приведения типов. Поскольку *this всегда является вызывающим объектом, можно вызывающий объект *this привести к типу данных «ссылка на базовый класс» и тем самым получить объект базового класса, который находится в текущем объекте производного класса.

Пример. Закрытое наследование

#include <iostream> #include <string> // Класс двигатель class Engine { private:

int power; // Мощность двигателя public:

Engine(int power = 100) { this >power = power; }

32

// Метод обеспечивает старт двигателя

void start() { std::cout << "Двигатель завелся!\n"; } }; // Класс трансмиссия (коробка передач в данном случае)

class Transmission { public:

// Включить передачу через параметр p

void shift_gear(int p = 0) { std::cout << "Переключили на "<< p << " передачу!\n"; }

};

/*

Класс машина, так как машина имеет двигатель и трансмиссию, используется закрытое множественное наследование от классов

engine и transmission */ /*

Необходимо помнить, что если не указать один из модификаторов (public, private, protected), то по

умолчанию

будет использовать private */

class Car : Engine, Transmission { private:

std::string mark; // Марка машины public:

/*

Конструктор класса Car принимает марку и мощность. Мощность

в конструкторе двигателя класса engine, а в транмиссии transmission используется конструктор по умолчанию. */

Car(std::string mark, int power) : engine(power) { this >mark = mark; // устанавливают марку автомобиля

Engine::start(); // метод запуска двигателя (engine) Transmission::shift_gear(1); // метод установки

передачи

}

};

33

Глава 19. ШАБЛОНЫ КЛАССОВ

19.1. Определение шаблона класса

Шаблоны классов дают возможность отделять алгоритм от типов данных, с которыми он работает. Шаблоны классов позволяют создавать множество родственных классов, которые применяются к любому типу данных, передаваемому в качестве параметра. Достоинство использования шаблонов состоит в том, что если алгоритм работы с данными разработан и проверен, то он может использоваться с данными любого типа без изменения кода.

Шаблон выглядит следующим образом:

template <typename T> class Student { T name;

………

}

Здесь T – параметр типа, играет роль заполнителя реального типа, который будет указан позднее. Этот параметр может иметь любое допустимое в С++ имя, но обычно применяют T или Type. В данном контексте слово typename можно заменить словом class.

Например:

template <class T> class Student {…}

Пример. Разработка шаблонного класса Stack.

// stacktemp.h – Шаблонный класс Stack #ifndef STACKTEMP_H_

#define STACKTEMP_H_ /*

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

*/

template<class Type>

34

class Stack { private:

int MAX = 10; // Максимальное количество элементов стека Type items[MAX]; // Массив размером MAX хранит элементы стека

int top; /* top указывает на голову стека, т.е. содержит номер элемента, следующего за последним

элементом

массива arr

*/

public:

Stack(int max = 10); // Конструктор с одним необязательным параметром

bool isempty(); // Проверка на пустоту стека bool isfull(); // Проверка на полноту стека

bool push(const Type & item); // Добавление элемента в стек

bool pop(Type & item); // Удаление элемента из стека

};

template<class Type> Stack<Type>::Stack(int max) {

MAX = max; top = 0;

}

template<class Type>

bool Stack<Type>::isempty() { return top == 0; }

template<class Type>

bool Stack<Type>::isfull() { return top == MAX; }

template <class Type>

bool Stack<Type>::push(const Type & item) { if (top < MAX) {

items[top++] = item; return true;

} else return false;

35

}

template<class Type>

bool Stack<Type>::pop(Type & item) { if (top > 0) {

item = items[ top]; return true;

} else return false;

}

#endif

19.2.Параметры шаблона

Вкачестве параметров шаблона используют типы, как стандартные, такиопределенныепользователем, шаблоныи переменные.

Вшаблонах так же, как и в функциях, можно задать параметр типа по умолчанию:

template <class T1, class T2 = int> class Car {...};

Кроме того, в шаблонах возможно использовать нетипизированные аргументы:

// Шаблон использует нетипизированный аргумент n template<class Type, int n>

class ArrayTP { private:

Type arr[n];

};

Также существует возможность использовать шаблоны как параметры:

// Шаблонный класс Car принимает другой шаблонный класс как // параметр

template <template <typename T> class Motor> class Car {...}

Таким образом, если имеется шаблон template <typename Type> Engine {...}, то можно сделать следующим образом:

36

Car<Engine> obj; // Создали объект шаблонного класса Car и

//передали шаблон Engine как параметр

19.3.Универсальность шаблонов

Вшаблонных классах можно применять те же приемы программирования, что и в обычных классах. Шаблонные классы могут выступать как в качестве базовых классов, так и в качестве компонентов других классов:

//Шаблонный класс Arr, представляющий статический массив template <typename Type>

class Arr {…} /*

Шаблонный класс DynamicArr наследуется от шаблоного класса статического массива Arr и представляет собой динамический массив

*/

template <typename Ty>

class DynamicArr : public Array<Ty> {…} /*

Шаблонный класс Stack использует шаблонный класс Arr для хранения своих элементов

*/ template<typename T> class Stack {

Arr<T>arr; // Класс Stack использует Arr в качестве

//компонента

}

// Массив стеков значений double Arr<Stack<double>> arrd;

37

Глава 20. ИСКЛЮЧЕНИЯ

20.1.Механизм исключений

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

Для управления исключениями доступны три компонента:

генерация исключения;

перехват исключения обработчиком;

использование блока try.

Программа генерирует исключение, когда возникает проблемная ситуация. Для перехвата таких ситуаций используется обработчик исключений catch. Обработчик исключений начинается с ключевого слова catch, за которым в круглых скобках следует объявление типа, представляющее тип исключения, которому оно соответствует. За ними в фигурных скобках располагается блок кода, выполняющего необходимые действия в зависимости от вида исключения.

Блок try представляет собой блок кода, в котором активизируются исключения. За ним следуют один или несколько блоков catch. Блок try начинается с ключевого слова try, а за ним в фигурных скобках находится код, в котором отслеживаются исключения.

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

Пример. Демонстрация механизма исключений

#include "stdafx.h" #include <iostream>

using namespace std;

38

int relation(int a, int b) { // Генерация исключения

if (a == –b) throw "Bad relation() arguments: a = –b not allowed!";

return a * b / (a + b);

}

int main() { try {

relation(5, –5);

} catch (const char* s) { // Перехват исключения

cerr << s << endl; // Вывод сообщения об ошибке на экран

abort(); // Вызов функции остановки программы

}

return 0;

}

20.2. Потеря исключений

Исключения, которые не обработаны, делятся на две группы:

Непредвиденное исключение, когда исключение не соответствует типу спецификации и по умолчанию приводит к остановке программы [14].

Неперехваченное исключение, когда исключение не перехвачено в силу отсутствия соответствующего catch и по умолчанию приводит к остановке программы [14].

Неперехваченное исключение не приводит к немедленному останову программы. Вместо этого программа сначала обращается

кфункции terminate(). По умолчанию функция terminate() вызывает функцию abort().

Можно изменить поведение функции terminate(), зарегистрировав функцию, которую terminate() будет вызывать вместо abort(). Для этого необходимо вызвать функцию set_terminate(terminate_ handler). Обе функции terminate() и set_terminate() объявлены в за-

головочном файле exception:

39

// Создание синонима handler для функции типа void f() typedef void (*handler)();

/*

Прототип функции set_terminate принимает один параметр f типа

handler и возвращает его.

*/

handler set_terminate(handler f) noexcept;

void terminate() noexcept; // Прототип функции terminate

Ключевое слово noexcept говорит о том, что функция не генерирует никаких исключений.

Непредвиденное исключение имеет аналогичный подход, но используются функции unexpected() и set_unexpected(). По умолча-

нию функция unexpected() вызывает функцию terminate().

20.3.Класс exception

Всовременных компиляторах С++ исключения входят в состав языка. Так, в заголовочном файле exception (exception.h) определен класс exception, который служит в С++ базовым классом для других классов исключений. В коде можно использовать объект exception или применять класс exception в качестве базового класса для собственных типов исключений. Среди виртуальных функций-членов имеется функция what(), возвращающая строку, природа которой зависит от реализации. Поскольку этот метод виртуальный, его можно переопределить в производном классе.

Пример. Использование класса exception

#include "stdafx.h" #include <iostream>

using namespace std;

int relation(int a, int b) { /*

Если a равно –b, то с помощью throw генерируется исключение

40

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