Лекции.Орг


Поиск:




Категории:

Астрономия
Биология
География
Другие языки
Интернет
Информатика
История
Культура
Литература
Логика
Математика
Медицина
Механика
Охрана труда
Педагогика
Политика
Право
Психология
Религия
Риторика
Социология
Спорт
Строительство
Технология
Транспорт
Физика
Философия
Финансы
Химия
Экология
Экономика
Электроника

 

 

 

 


Исключения в конструкторах и деструкторах




Из конструктора и деструктора нельзя возвращать значение. Механизм исключений дает возможность сообщить об ошибке, возникшей в конструкторе или деструкторе объекта.

Пример: класс Vector, в котором ограничивается количество запрашиваемой памяти:

class Vector{

public:

class Size{}; // Класс исключения

enum {max = 32000}; // Максимальная длина вектора

Vector (int n){ // Конструктор

if (n<0 || n>max) throw Size();

...

}

...

};

При использовании класса Vector можно предусмотреть перехват исключений типа Size:

try{

Vector *р = new Vector(i);

...

}

catch (Vector::Size){

... // Обработка ошибки размера вектора

}

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

Если в конструкторе объекта генерируется исключение, автоматически вызываются деструкторы:

· для полностью созданных в этом блоке объектов,

· полей данных текущего объекта, которые являются объектами,

· для его базовых классов.

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

Иерархии исключений функции

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

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

Пример: классы исключений в математической библиотеке.

class Matherr{};

class Overflow: public Matherr{}; // Переполнение

class Underflow: public Matherr{}; // Исчезновение порядка

class ZeroDivide: public Matherr{}; // Деление на ноль

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

class IOerr{};

class Readerr: public IOerr{}; // Ошибка чтения

class Writerr: public IOerr{}; // Ошибка записи

class Seekerr: public IOerr{}; // Ошибка поиска

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

Существует ряд стандартных исключений, которые генерируются операциями или функциями C++. Они являются производными от библиотечного класса exception, описанного в заголовочном файле <stdexcept>. Например, операция new при неудачном выделении памяти генерирует исключение типа bad_alloc.

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

 

Тема 2.16

Преобразование типов: преобразование с помощью функций языка С++

 

Для выполнения явных преобразований типа в С++ кроме операции приведения типа, унаследованной от языка С, существует группа операций – const_cast, dynamic_cast, reinterpret_cast и static_cast.

Операция const_cast служит для удаления модификатора const.

Формат операции:

const_cast <тип> (выражение);

Указанный тип должен быть таким же, как и тип выражения, за исключением модификатора const (обычно это указатель).

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

Пример:

void print (int *p) { // Функция не изменяет значение *р

cout << *р;

}

const int *p;

print(p); // Ошибка, поскольку р объявлен как указатель на константу

 

void print (int *p) { // Функция не изменяет значение *р

cout << *р;

}

const int *p;

print(const_cast<int*>p); // Верно, указатель на константу преобразован в обычный

Операция const_cast используется в том случае, когда программист уверен, что в теле функции значение, на которое ссылается указатель, не изменяется. Естест­венно, если есть возможность добавить к описанию формального параметра мо­дификатор const, это предпочтительнее использования преобразования типа при вызове функции.

Операция static_cast используется для преобразования типа на этапе компиля­ции между:

· целыми типами,

· целыми и вещественными типами,

· целыми и перечисляемыми типами.

Кроме того, операция static_cast используется при наследовании.

Формат операции:

static_cast <тип> (выражение);

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

Пример:

float f = 100;

int i = static_cast <int> (f); // целые и вещественные имеют различное внутреннее представление

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

Операция reinterpret_cast применяется для преобразования не связанных между собой типов (например, указателей в целые числа), а также указателей типа void* в конкретный тип. При этом внутреннее представление данных остается неизменным, а изменяется только точка зрения компилятора на данные.

Формат операции:

reinterpret_cast <тип> (выражение);

Тип может быть ссылкой, указателем, целым или вещественным типом.

Пример:

char *р = reinterpret_cast <char*>(malloc(100));

long l = reinterpret_cast <long>(p);

Различие между операциями static_cast и reinterpret_cast состоив в том, что первая операция производит проверку допустимости преобразования, а вторая нет. Это позволяет компилятору производить минимальную проверку при использовании static_cast, а программисту - обозначать опасные преобразования с помощью reinterpret_cast.

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

Формат операции:

dynamic_cast <тип *> (выражение);

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

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

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

Повышающее преобразование

Выполнение с помощью операции dynamic_cast повышающего преобразования равносильно простому присваиванию:

class В { / *... * / };

class С: public В { /*... */ };

С* с = new С;

В* b = dynamic_cast<B*>(c); // Эквивалентно В* b = с;

Понижающее преобразование

Чаще всего операция dynamic_cast применяется при понижающем преобразовании - когда компилятор не имеет возможности проверить правильность приведения.

Производные классы могут содержать функции, которых нет в базовых классах. Для их вызова через указатель базового класса нужно иметь уверенность, что этот указатель ссылается на объект производного класса. Такая проверка производится в момент выполнения приведения типа с использованием механизма RTTI (run-time type information) - «информации о типе во время выполнения программы». При этом аргумент операции dynamic_cast должен быть полиморфного типа, т.е. иметь хотя бы один виртуальный метод.

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

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

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

Для использования механизма RTTI необходимо подключить к программе заголовочный файл typeinfo.h и установить соответствующий режим компилятора.

Пример:

Описан полиморфный базовый класс В и производный от него класс С, в котором определена функция f2. Операция dynamic_cast с проверкой результата преобразования используется для того, чтобы вызывать f2 из функции demo, определенной в глобальной области видимости, только в случае, когда последней передается указатель на объект производного класса.

#include <iostream.h>

#include <typeinfo.h>

class B{

...

public: virtual void f1 () {...};

};

class C: public B {

...

public: void f2() {cout << "f2";}

};

void demo (B* p) {

C* с = dynamic_cast<C*>(p);

if (c) c->f2();

else cout << "Передан не класс С";

}

 

int main(){

В* b = new В;

demo(b); // Выдается сообщение "Передан не класс С"

С* с = new С:

demo(c); // Выдается сообщение "f2" (правильно)

return 0;

}

Если в этом примере вместо dynamic_cast использовать приведение типов в стиле С:

С* с = (С*) р;

проконтролировать допустимость операции невозможно. Если указатель р не ссылается на объект класса С, это приведет к ошибке.

Другим недостатком приведения в стиле С является невозможность преобразования в производный виртуального базового класса - это запрещено синтаксически. С помощью операции dynamic_cast такое преобразование возможно при условии, что класс является полиморфным и преобразование недвусмыслерню.

Пример: выполняется понижающее преобразование виртуального базового класса.

В данном примере, реализуется следующая иерархия классов:

#include <iostream.h>

#include <typeinfo.h>

class A{

...

public: virtual ~A(){... };

};

class B: public virtual A{... };

class C: public virtual A{... };

class D: public B, public C{... };

void demo(A *a) {

D* d = dynamic_cast<D*>(a);

if (d) {... }

}

int main(){

D *d = new D;

demo(d);

return 0;

}

Преобразование ссылок

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

Корректность приведения проверяется автоматически, в случае несовпадения порождается исключение bad_cast.

Пример:

#include <iostream.h>

#include <typeinfo.h>

class B{

...

public: virtual void fl () {... };

};

class C: public B{

...

public: void f2(){... };

};

 

 

void demo (B& p) {

try{

C& с = dynamic_cast<C&>(p);

c.f2();

}

catch (bad_cast) {

...

}

}

 

int main(){

B* b = new B;

demo(*b); // Порождается исключение

С* с = new С;

demo(*c); // Правильно

return 0;

}





Поделиться с друзьями:


Дата добавления: 2016-11-12; Мы поможем в написании ваших работ!; просмотров: 1545 | Нарушение авторских прав


Поиск на сайте:

Лучшие изречения:

Люди избавились бы от половины своих неприятностей, если бы договорились о значении слов. © Рене Декарт
==> читать все изречения...

2504 - | 2303 -


© 2015-2025 lektsii.org - Контакты - Последнее добавление

Ген: 0.008 с.