МЕТОДЫ ПОЗИЦИОНИРОВАНИЯ УКАЗАТЕЛЯ В ФАЙЛЕ



Классы istream и ostream содержат методы для позиционирования указателя файла. Под указателем понимается порядковый номер следующего байта в файле, который должен быть считан или записан. Любой объект класса istream имеет указатель «get», который показывает номер в файле очередного вводимого байта. Любой объект класса ostream имеет указатель

«put», который показывает номер в файле очередного выводимого байта.

Функция

istream& seekg(streamoff offset, ios::seek_dir origin);

при извлечении данных из файла перемещает указатель на offset байт от позиции, заданной параметром origin.

Функция

ostream& seekp(streamoff offset, ios::seek_dir origin);

во время чтения данных из файла перемещает указатель на offset байт от позиции, заданной параметром origin.

Параметр offset должен принимать целое значение.

Параметр origin представляет собой перечисление, которое имеет следующие значения:

ios::beg – смещение от начала;

ios::cur – смещение от текущей позиции;

ios::end – смещение от конца.

Функции

ostream& seekp(streampos position); istream& seekg(streampos position);

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

Функция

streampos tellg();

возвращает значение указателя взять из потока «get».

Функция

streampos tellp()

возвращает значение указателя поместить в поток «put». Например,

istream FileObject;

FileObject . seekg ( n ); //позиционирование FileObject на n-й байт от начала файла

FileObject . seekg ( n , ios :: cur ); //позиционирование FileObject на n байтов вперед

FileObject . seekg ( k , ios :: end ); //позиционирование FileObject на k-й байт от конца файла

FileObject . seekg (0, ios :: end ); //позиционирование FileObject на конец

файла

location = FileObject . tellg ();//присваивание   переменной   location

значения файлового указателя «get»

 

Файлы произвольного доступа

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

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

 

ШАБЛОНЫ ФУНКЦИЙ И КЛАССОВ

Шаблоны – одно из последних нововведений стандарта языка C++. Шаблоны дают возмжоность определять при помощи одного фрагмента кода целый набор взаимосвязанных функций (перегруженных), называемых шаблонными функциями, или набор связанных классов, назыввемых шаблонными классами.

Например, можно написать один шаблон функции сортировки массива, на основе которого C++ будет автоматически генерировать отдельные шаблонные функции, сортирующие массивы типов int, float, массив строк и т.д. Или достаточно описать один шаблон класса стеков, а затем C++ будет автоматически создавать отдельные шаблонные классы типа стек целых, стек вещественных чисел, стек строк и т.д.

 

Шаблоны функций

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

Все описания шаблонов функций начинаются с ключевого слова template, за котором следует список формальных параметров шаблона, заключаемый в угловые скобки (< и >); каждому формальному параметру должно предшествовать ключевое слово class или typename. Например,

template <class T>

или

template <typename ElementType>

или

template <class BorderType, class FillType>

Формальные параметры в описании шаблона используются (наряду с параметрами встроенных типов или типов, определяемых пользователем) для определения типов параметров функции, типа возвращаемого функцией значения и типов переменных, объявляемых внутри функции. Далее, за этим заголовком, следует обычное описание функции. Ключевое слово class или typename, используемое в шаблоне функции при задании типов параметров, означает «любой встроенный тип или тип, опеределяемый пользователем».

Замечание. Ошбикой является отсутствие ключевого слова class (или

typename) перед каждым формальным параметром типа шаблона функции. template <class T>

T& inc_value(T &val)

{

++val; return val;

}

int main()

{

int x = 0; x=(int)inc_value<int>(x); cout<<x<<endl;

char c = 0; c=(char)inc_value<char>(c); cout<<c<<endl;

return 0;

}

template <class T1>

void PrintArray(const T1* array, const int count)

{

for (int i=0; i<count; i++) cout<<array[i]<<" ";

cout<<endl;

}

В шаблоне функции PrintArray() объявляется формальный параметр T1 для массива, который будет выводиться функцией PrintArray().

T1* называется параметром типа. Когда компилятор обнаруживает в тексте программы вызов функции PrintArray(), он заменяет T1 во всей области определения шаблона тип первого параметра функции PrintArray() и С++ создает шаблоную функцию вывода массива указанного типа данных. После этого вновь созданная функция компилируется.

int main()

{

const int aCount=5, bCount=7, cCount=6; int a[aCount]={1,2,3,4,5};

double b[bCount] = {1.1, 2.2, 3.3, 4.4, 5.5, 6.6, 7.7};

char c[cCount]="HELLO"; //6-я позиция для null cout<<"Array a:"<<endl;

PrintArray(a,aCount); //шаблон для integer cout<<"Array b:"<<endl; PrintArray(b,bCount); //шаблон для double

cout<<"Array c:"<<endl; PrintArray(c,cCount); //шаблон для character return 0;

}

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

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

void PrintArray(const int*, const int); void PrintArray(const double*, const int); void PrintArray(const char*, const int);

которые используют один и тот же код, кроме кода для типа T1.

 

Перегрузка шаблонных функций

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

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

template <class T1>

void PrintArray(const T1* array, const int count)

{

for (int i=0; i<count; i++) cout<<array[i]<<" ";

cout<<endl;

}

template <class T1>

void PrintArray(const T1* array, const int lowSubscript, const int highSubscript)

{

for (int i=lowSubscript; i<=highSubscript; i++) cout<<array[i]<<" ";

cout<<endl;

}

int main()

{

const int aCount=5, bCount=7, cCount=6; int a[aCount]={1,2,3,4,5};

double b[bCount] = {1.1, 2.2, 3.3, 4.4, 5.5, 6.6, 7.7};

char c[cCount]="HELLO"; //6-я позиция для null

cout<<"Array a from 1 to 3:"<<endl; PrintArray(a,0,2); //шаблон для integer cout<<"Array b from 4 to 7:"<<endl; PrintArray(b,3,6); //шаблон для double cout<<"Array c from 3 to 5:"<<endl; PrintArray(c,2,4); //шаблон для character return 0;

}

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

template <class T1>

void PrintArray(const T1* array, const int count)

{…}

template <class T1>

void PrintArray(const T1* array, const int lowSubscript, const int highSubscript)

{…}

void PrintArray(char* array, const int count)

{

for (int i=0; i<count; i++) cout<<array[i]<<endl;

}

 

int main()

{

const int cCount=6;

char c[cCount]="HELLO"; //6-я позиция для null cout<<"Array c:"<<endl;

PrintArray(c,cCount);

… return 0;

}

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

Замечание. Компилятор ищет шаблон, полностью соотвествующий вызываемой функции по типу всех параметров; автоматическое преобразование типов не производится (возможно, char к int, double к int и т.д.).

 

Шаблоны классов

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

Рассмотрим простой шаблон класса stack (стек). Понятие «стек» не зависит от типа данных, которые помещаются в стек. Тип данных должен быть задан только тогда, когда приходит время создать стек «фактически». Поэтому достаточно создать некое общее описание понятия стека и на основе этого родового класса создавать классы, являющиеся специфическими версиями для конкретного типа данных.

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

template<class T> class stack

{

T* v; T* p; int sz;

public:

stack(int s) { v = p = new T[sz=s]; }

~stack() { delete[] v; }

void push(T a) { *p++ = a; } T pop() { return *--p; }

int size() const { return p-v; }

};

Префикс template<class T> указывает, что описывается шаблон класса с параметром T, обозначающим тип классов Stack которые будут создаваться на основе этого шаблона. Идентификатор T определяет тип данных– элементов, хранящихся в стеке.

Имя шаблонного класса, за которым следует тип, заключенный в угловые скобки <>, является именем класса (определяемым шаблоном класса), и его можно использовать как все имена класса. Например, ниже определяется объект sc класса stack<char>:

stack<char> sc(100); // стек символов

Если не считать особую форму записи имени, класс stack<char> полностью эквивалентен классу определенному так:

class stack_char {

char* v; char* p; int sz;

public:

stack_char(int s) { v = p = new char[sz=s]; }

~stack_char() { delete[] v; } void push(char a) { *p++ = a; }

char pop() { return *--p; }

int size() const { return p-v; }

};

Имея определение шаблонного класса stack, можно следующим образом определять и использовать различные стеки:

typedef struct _Point

{int x,y;}Point; class shape {};

void f(stack<Complex>& sc) // параметр типа 'ссылка на complex'

{


 

памяти


 

stack<shape*> ssp(200); // стек указателей на фигуры stack<Point> sp(400);                   // стек структур Point sc.push(Complex(1,2));

Complex z=sc.pop(); z*2.5;

cout<<z;

stack<int>*p = 0;  // указатель на стек целых

p = new stack<int>(800); // стек целых размещается в свободной

 

for ( int i = 0; i<400; i++)

{


p->push(i); Point p1; p1.x=i; p1.y=i+400;

sp.push(p1);

}

cout<<sp.size()<<endl;

//…

}

В случае внешней реализацию методов шаблона классов stack сам шаблон определяется следующим образом:

template<class T> class stack

{

T* v; T* p; int sz;

public:

stack(int);

~stack(); void push(T); T pop();

int size() const;

};

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

template<class T> void stack<T>::push(T a)

{*p++ = a;}

template<class T> T stack<T>::pop()

{return *--p;}

template<class T> int stack<T>::size() const

{return p-v;}

template<class T> stack<T>::stack(int s)

{v = p = new T[sz=s];} template<class T> stack<T>::~stack()

{delete[] v;}

Для приведенного примера (функция f) компилятор должен создать определения конструкторов для классов stack<shape*>, stack<Point>, stack<Complex> и stack<int>, деструкторов для stack<Complex>, stack<shape*> и stack<Point>, версии функций push() для stack<complex>, stack<int> и stack<Point> и версию функции pop() для stack<complex>. Такие создаваемые функции будут совершенно обычными методами, например:

void stack<complex>::push(complex a) { *p++ = a; }

Здесь отличие от обычного метода только в форме имени класса. Точно так же, как в программе может быть только одно определение метода класса, возможно только одно для метода шаблонного класса. Е

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

 

Шаблоны класса для списка

template<class type> class CListTemplate

{

struct SNode

{

type *data;

SNode *next;

};

SNode *head,*p; public:

CListTemplate() {head=p=0;} void AddElem(type *d);

bool RemoveElem(type *d); void DisplayList();

void clear();

~CListTemplate(){clear();}

};

template<class type>

void CListTemplate<type>::AddElem(type *d)

{

if(head)

{

p->next = (SNode*)malloc(sizeof(SNode)); p = p->next;

}

else

{

head = (SNode*)malloc(sizeof(SNode)); p=head;

}

p->data = d; p->next = 0;

}

template<class type>

bool CListTemplate<type>::RemoveElem(type *d)

{

SNode *curr = head; SNode *p1;

if (*(head->data)==*d)

{

p1=head; head=head->next; delete p1;

return true;

}

while(curr->next && *(curr->next->data)!=*d) curr=curr->next;

if (curr->next)

{

``p1=curr->next;

curr->next=curr->next->next;

delete p1; return true;

}

else

return false;

}

template<class type>

void CListTemplate<type>::DisplayList()

{

SNode *curr=head; while (curr)

{

cout<<*(curr->data)<<" "; curr=curr->next;

}

cout<<endl;

}

template<class type>

void CListTemplate<type>::clear()

{

SNode *curr=head; while(head)

{

curr=head; head=head->next; delete curr;

}

}

int main()

{

CListTemplate<Complex> cl; CListTemplate<int> c_int; for (int i=0;i<10; i++)

{

cl.AddElem(new Complex(10.0f*i,10.0f+i)); c_int.AddElem(new int(10*i));

}

cl.DisplayList();

Complex c_ob(40.0f,14.0f); cl.RemoveElem(&c_ob); cl.DisplayList(); c_int.DisplayList();

i=90;

c_int.RemoveElem(&i);

c_int.DisplayList(); return 0;}

Шаблоны классов и нетиповые параметры

Шаблон класса stack, рассмотренный ранее, использовал только параметр типа в заголовке шаблона. Но в шаблонее имеется возможность использовать и нетиповые параметры. Например, в заголовок шаблона можно добавить нетиповой параметр int elements, который будет содержать колчиество элементов стека:

template <class T, int elements> Тогда оператор

Stack <double, 100> s1;

задает шаблонный класс Stack с именем s1, состоящий из 100 элементов типа double.

Теперь в описании шаблона класса вместо объявлений в разделе закрытых членов-данных

T* v;

T* p;

необходимо поместить следующее:

T p[elements]; int count;

Замечание. Если можно определить размер класса-контейнера (такого как массив или стек) во время компиляции (нарпимер, при помощит нетипового параметра), это повысит скорость выполнения программы, устранив потери времени на динамическое выделение памяти при выполнении программы.

 

Вложенные шаблонные классы

Объявление вложенного шаблонного  класса  выглядит следующим образом:

template<class ta> class A

{

public:

template<class tai> class AI

{

public:

AI(ta ta1,tai tai1):ta_(ta1),tai_(tai1){} private:

ta ta_;

tai tai_;

};

};

int main()

{

A<int> a;

A<int>::AI<int> ai(10,20); return 0;

}

В функции main() показана форма вызова конструктора вложенного шаблонного класса.

Пример вызов функций из вложенных шаблонных классов внутри родительского класса:

template<class ta> class A

{

public:

template<class tai> class AI

{

public:

void func(){}

};

void myfunc()

{

AI<double> a; a.func();

}

};

Добавим в консруктор вложенного класса параметры: template<class ta>

class A

{

public:

template<class tai> class AI

{

public:

AI(const ta &v1,const tai &v2){}

};

void myfunc()

{

AI<double> a; a.func();

}

};

};

int main()

{

A<int> a;

//a.myfunc(); return 0;

}

Пример успешно откомпилируется. Но если раскомментировать строку с функцией a.myfunc(), то компилятор выдаст ошибку. В данном случае имеет место следующая особенность шаблонов: компилятор не проверяет ни параметры, ни вызываемые функции до тех пор, пока они не используются.

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

template<class ta> class A

{

public:

template<class tai> class AI

{

public:

AI(const ta &v1,const tai &v2)

{

v1.showValue(); v1.checkValue(10);

}

};

public:

void myfunc()

{

AI<double> a(100,20.3);

}

};

int main()

{

A<int> a;

//a.myfunc(); return 0;

}

В теле конструктора вложенного шаблонного класса AI(const ta &v1,const tai &v2) вызываются две функции showValue() и checkValue(10) от типа-шаблона, причем в данном случае компилятор не проверяет, есть ли у

данного типа такие функции-члены. Раскомментировав в функции main() строку a.myfunc(), мы получим ошибку компиляции. А теперь добавим к этому примеру определение структуры SBase:

struct SBase

{

int val;

SBase(int v=0):val(v){}

void showValue()const{ printf("value: %d\n",val); } void checkValue(int)const{}

};

template<class ta=SBase> class A

{

public:

template<class tai> class AI

{

public:

AI(const ta &v1,const tai &v2)

{

v1.showValue(); v1.checkValue(10);

}

};

public:

void myfunc()

{

AI<double> a(100,20.3);

}

};

int main()

{

A<SBase> a; a.myfunc(); A<> a1;

a1.myfunc(); return 0;

}

В строке AI<double> a(100,20.3) в качестве первого параметра передано число, т.е. предполагается, что шаблонный тип имеет конструктор с типом int

– в нашем случае именно для этого определен конструктор структуры SBase

– SBase(int v=0):val(v){}.

Теперь рассмотрим такой пример: template<class T>

class A

{

template<class T1> class Iterator

{

private:

void calc(){}

};

public:

void func()

{

Iterator<int> it; it.calc();

}

};

int main()

{

A<double> a; a.func(); return 0;

}

В этом примере была предпринята попытка обратится к private- функции вложенного класса - компилятор, естественно, нам этого не позволил. Чтобы исправить ситуацию нужно объявить родительский класс дружественным для вложенного. Делается это так:

template<class T> class A

{

template<class T1> class Iterator

{

friend class A<T>; private:

void calc(){}

};

public:

void func()

{

Iterator<int> it; it.calc();

}

};

int main()

{

A<double> a; a.func(); return 0;

}

Теперь все работает.

 

Наследование шаблонных классов

Механизм наследования шаблонных классов практически ничем не отличается от механизма наследования обычных классов, отличия только в необходимости объявлять шаблонные типы-параметры. Смотрим пример:

template<class T> class A

{

public:

A(const T &t_):t(t_){} private:

T t;

};

template<class T1> class B : public A<T1>

{

public:

B(const T1 &t_):A<T1>(t_){}

};

int main()

{

B<double> b(10.0); return 0;

}

Рассмотрим применение на практике полезных особенностей шаблонных классов.

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

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

2. Использование шаблона. Пример: class A

{

public:

virtual void print(){ printf("A::print()\n"); }

};

class B

{

public:

virtual void print(){ printf("B::print()\n"); }

};

class C

{

public:

virtual void print(){ printf("C::print()\n"); }

};

template<class T> class D : public T

{

public:

virtual void print(){ printf("template D::print()\n"); }

};

int main()

{

D<A> a1;

a1.print(); // template D::print() D<B> b1;

b1.print(); // template D::print() D<C> c1;

c1.print();// template D::print() return 0;

}

Рассморитм второй пример участия шаблонов в иерархии классов: class A

{

public:

void calc(){}

};

template<class T> class B : public T

{

public:

void print(){}

};

class C : public B<A>

{

public:

void anyfunc(){}

};

int main()

{

C c; c.print();

c.anyfunc();

c.calc(); return 0;

}

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

– шаблон класса может быть производным от шаблонного класса;

– шаблон класса  может  являться производным от нешаблонного класса;

– шаблонный класс может быть производным от шаблона класса;

– нешаблонный класса может быть производным от шаблона класса.

 

 

Шаблоны и друзья

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

Оформление отношений дружественности достаточно сложно.

Ели внутри шаблона класса X, который объявлен как template <class T> class X

находится объявление дружественной функции friend void f1();

то функция f1 является дружественной для каждого шаблонного класса, полученного из данного шаблона.

Если внутри шаблона класса X, который объявлен как template <class T> class X

находится объявление дружественной функции в форме friend void f2(X <T> &);

то для конкретного типа T, например, float, дружественной для класса X<float> будет только функция f2(X <float> &).

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

template <class T> class X

объявляется дружественная функция в форме friend void A::f4();

то функция-член f4 класса A будет дружественной для каждого шаблонного класса, полученного из данного шаблона.

Внутри шаблона класса X, который был объявлен объявлен следующим образом:

template <class T> class X

объявление дружественной функции в виде friend void C<T>::f5(X <T>&);

для конкретного типа T, например int, сделает функцию-член C<int>::f5(X <int>&)

другом только шаблонного класса X<int>. Внутри шаблона класса X, объявленного как template <class T> class X

можно обяъвить дружественный класс Y friend class Y;

В результате чего каждая из функций-членов класса Y будет дружественной для каждого шаблонного класса, произведенного из шаблона класса X.

Если внутри шаблона класса X, который был объявлен как template <class T> class X

объявлен второй класс Z в виде friend class Z<T>;

то при создании шаблонного класса с конкретным типом T, например, типом double, все члены класса Z<double> будут друзьями шаблонного класса X <double>.

 

Шаблоны и статические члены

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

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

template<class T> class A {

public:

static int a;

};

template<class T> int A<T>::a=10;

int main() {

A <double> b1; b1.a=20;

A <double> b2; b2.a=50;

cout<<b1.a<<endl; //50 A <char> b3; cout<<b3.a<<endl;

A <char> b4; b4.a=b3.a*2; cout<<b3.a<<endl; return 0;

}

 

ОБРАБОТКА ИСКЛЮЧИТЕЛЬНЫХ СИТУАЦИЙ

Исключительная ситуация, или исключение — это возникновение непредвиденного или аварийного события, которое может порождаться некорректным использованием аппаратуры. Например, это деление на ноль или обращение по несуществующему адресу памяти. Обычно эти события приводят к завершению программы с системным сообщением об ошибке. C++ дает программисту возможность восстанавливать программу и продолжать ее выполнение. Исключения C++ не поддерживают обработку асинхронных событий, таких, как ошибки оборудования или обработку прерываний, например, нажатие клавиш Ctrl+C. Механизм исключений предназначен только для событий, которые происходят в результате работы самой программы и указываются явным образом. Исключения возникают тогда, когда некоторая часть программы не смогла сделать то, что от нее требовалось. При этом другая часть программы может попытаться сделать что-нибудь иное.

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


Дата добавления: 2019-09-13; просмотров: 206; Мы поможем в написании вашей работы!

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






Мы поможем в написании ваших работ!