Скачиваний:
190
Добавлен:
05.07.2021
Размер:
16.53 Mб
Скачать

11. Исключение bad_alloc и операция new. Примеры использования. Использование нулевого указателя и операции new. Примеры использования.

В настоящее время в C++ проблемы, возникающие во время выделения памяти с помощью операции new, обрабатываются путем генерации в операторе new исключения типа bad_alloc. Заголовочный файл new включает объявление класса bad_alloc, открыто унаследованного от класса exception.

В более ранних версиях стандарта С++ операция new возвращала нулевой указатель, если не могла выделить запрошенный объем памяти.

Текущий подход демонстрируется в программе ниже. Если исключение перехвачено, то программа выводит зависящее от реализации сообщение, которое возвращается унаследованным методом what(), и затем завершается.

//Пример №10. Обработка исключения типа bad_alloc

#include <iostream>

#include <new>

#include <cstdlib>//для exit(), EXIT_FAILURE using namespace std; struct Company { double data[20000];

};

int Ex10() {

Company* company; try {

cout << "Попытка выделения большого блока памяти:\n"; //Попытка выделения большого блока памяти company = new Company[10000];//1 600 000 000 байт cout << "Возможно выполнение следующего запроса:

\n";//вывод результатов запроса new

}

catch (bad_alloc& ba) {

cout<<"Словлено исключение выделения

памяти!\n";//произошло исключение

cout << ba.what() << endl;

exit(EXIT_FAILURE);

}

cout << "Память успешно выделена\n"; company[0].data[0] = 4; cout << company[0].data[0] << endl; delete[] company;

return 0;

}

В этом случае метод what() возвращает строку "std::bad_alloc".

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

НУЛЕВОЙ УКАЗАТЕЛЬ И ОПЕРАЦИЯ NEW

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

int* ptrInt = new (std::nothrow) int;

int* ptrIntArr = new (std::nothrow) int[500];

Эта форма позволяет переписать основную часть кода следующим образом:

Company* company;

company = new (std::nothrow) Company[10000];//1 600 000 000 байт if (company == 0){

cout << "Невозможно выделить память!\n"; exit(EXIT_FAILURE);

}

12. Использование исключений вместе с классами и наследованием. Особенности использования вложенных классов.

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

Код в примере ниже предназначен для исследования таких возможностей. В этом заголовочном файле объявлен простой класс Sales, содержащий значение года, и массив из 12 ежемесячных объемов продаж.

Класс LabeledSales порожден от класса Sales и содержит дополнительный элемент для хранения метки данных.

//Пример №11. Использование исключений и наследования

//файл Sales.h

#pragma once

#include <iostream>

#include <exception>

#include <stdexcept> #include <string> using namespace std; class Sales { protected:

enum { MONTHS = 12 };//может быть статической константой public: class BadIndex : public std::logic_error { private: int badIndex; //недопустимое значение индекса public:

explicit BadIndex(int ix, const std::string& s =

"Ошибка индекса в объекте класса Sales\n"); int badIndexValue() const { return badIndex; } virtual ~BadIndex() noexcept {}

};

explicit Sales(int yy = 0);

Sales(int year, const double* gross, int n); virtual ~Sales() { } int Year() const { return year; } virtual double operator [] (int i) const; virtual double& operator [] (int i); private:

double gross[MONTHS]; int year;

};

class LabeledSales : public Sales { public: class BadIndexLabeled : public Sales::BadIndex { private:

std::string lbl; public:

BadIndexLabeled(const std::string& lb, int ix, const std::string& s = "Ошибка индекса в объекте класса

LabeledSales\n");

const std::string& label_val() const { return lbl; } virtual ~BadIndexLabeled() noexcept() {}

};

explicit LabeledSales(const std::string& lb = "none", int yy = 0);

LabeledSales(const std::string& lb, int yy, const double* gr, int n);

virtual ~LabeledSales() { }

const std::string& Label() const { return label; } virtual double operator[] (int i) const; virtual double& operator[] (int i); private:

std::string label;

};

//Файл sales.ерр #include "sales.h" using std::string;

Sales::BadIndex::BadIndex(int ix, const string& s) : std::logic_error(s), badIndex(ix) {}

Sales::Sales(int year) { this->year = year; for (int i = 0; i < MONTHS; ++i) gross[i] = 0;

}

Sales::Sales(int year, const double* gross, int n) { this->year = year;

int lim = (n < MONTHS) ? n : MONTHS;

int i;

for (i = 0; i < lim; ++i) this->gross[i] = gross[i]; //Для i > n и і < MONTHS for (; i < MONTHS; ++i)

this->gross[i] = 0;

}

double Sales::operator[] (int i) const{ if (i < 0 || i >= MONTHS) throw BadIndex(i);

return gross[i];

}

double& Sales::operator[] (int i){ if (i < 0 || i >= MONTHS) throw BadIndex(i); return gross[i];

}

LabeledSales::BadIndexLabeled::BadIndexLabeled(const string& lb, int ix,const string& s) : Sales::BadIndex(ix, s){

lbl = lb;

}

LabeledSales::LabeledSales(const string& lb, int yy): Sales(yy){ label = lb;

}

LabeledSales::LabeledSales(const string& lb, int yy,const double* gr, int n): Sales(yy, gr, n){ label = lb;

}

double LabeledSales::operator[] (int i) const{

if (i < 0 || i >= MONTHS) throw BadIndexLabeled(Label(), i); return Sales::operator[](i);

}

double& LabeledSales::operator[] (int i){ if (i < 0 || i >= MONTHS) throw BadIndexLabeled(Label(), i); return Sales::operator [] (i);

}

Символьная константа MONTHS расположена в защищенном разделе Sales, поэтому ее значение доступно для производных классов, таких как LabeledSales.

Класс bad_index находится в открытом разделе Sales; это делает его доступным в качестве типа для клиентских блоков catch. Правда, вовне этот тип нужно указывать как Sales::bad_index. Данный класс порожден от стандартного класса logic_error, он может сохранять недопустимые значения индексов и сообщать о них.

Класс nbad_index находится в открытом разделе LabeledSales и доступен в клиентском коде как LabeledSales::nbad_index.

Он порожден от bad_index и может дополнительно сохранять и выводить метки объектов LabeledSales. Поскольку класс bad_index порожден от logic_error, то и nbad_index также порожден от logic_error.

Оба класса содержат перегруженные методы operator [], которые предназначены для доступа к хранимым в объекте отдельным элементам массива и для генерации исключения, если индекс массива выходит за допустимые пределы.

Оба класса, bad_index и nbad_index, используют спецификацию исключения throw(). Причина в том, что оба они, в конечном счете, унаследованы от базового класса exception, виртуальный деструктор которого использует спецификацию исключения. Это характерно для С++98; в С++11 деструктор exception не имеет спецификации исключения.

В примере также показана реализация методов, которые не были встроенным образом определены в классе. Обратите внимание, что вложенные классы требуют многократного использования операции разрешения контекста. Учтите также, что функции operator [] генерируют исключения при выходе индекса за пределы массива.

Рассмотрим пример использования этих классов в программе, которая сначала пытается выйти за пределы массива в объекте LabeledSales по имени sales2, а затем — за пределы массива в объекте Sales по имени sales1. Эти попытки реализованы в двух разных блоках try, которые проверяют каждый вид исключений.

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

//Пример №12. Использование вложенных классов исключений

#include <iostream> #include "sales.h" int main() { system("chcp 1251"); system("cls"); using std::cout; using std::cin; using std::endl; double valsl[12] = {

1220, 1100, 1122, 2212, 1232, 2334, 2884, 2393, 3302, 2922, 3002, 3544 };

double vals2[12] = {

12, 11, 22, 21, 32, 34,

28, 29, 33, 29, 32, 35 };

Sales salesl(2011, valsl, 12);

LabeledSales sales2("Компания №1", 2012, vals2, 12);

cout << "Первый блок try: \n";//первый блок try

try {

int i;

cout << "Год = " << salesl.Year() << endl;

for (i = 0; i < 12; ++i) {

cout << salesl[i] << ' ';

if (i % 6 == 5)

cout << endl;

}

cout << "Год = " << sales2.Year() << endl; cout << "Метка = " << sales2.Label() << endl;

for (i = 0; i <= 12; ++i) {

cout << sales2[i] << ' ';

if (i % 6 == 5)

cout << endl;

}

cout << "Конец первого блока try.\n";

}

catch (LabeledSales::BadIndexLabeled & bad) {

cout << bad.what();

cout << "Компания: " << bad.label_val() << endl;//компания

cout << "Неверный индекс: " << bad.badIndexValue() << endl;//недопустимый индекс

}

catch (Sales::BadIndex & bad) { cout << bad.what();

cout << "Неверный индекс: " << bad.badIndexValue() << endl;//недопустимый индекс

}

cout << "\nСледующий блок try: \n";//второй блок try

try {

sales2[2] = 37.5;

salesl[20] = 23345;

cout << "Конец второго блока try.\n";//конец второго блока try

}

catch (LabeledSales::BadIndexLabeled & bad) {

cout << bad.what();

cout << "Компания: " << bad.label_val() << endl;//компания

cout << "Неверный индекс: " << bad.badIndexValue() << endl;//недопустимый индекс

}

catch (Sales::BadIndex & bad) { cout << bad.what();

cout << "Неверный индекс: " << bad.badIndexValue() << endl;//недопустимый индекс

}

cout << "Программа выполнена\n";

return 0;

}