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

2vcTnguyvU

.pdf
Скачиваний:
6
Добавлен:
15.04.2023
Размер:
955.27 Кб
Скачать

int hide; };

//спрятанная

 

typedef

void (X:: *pfcn) ( );

 

int main(int argc, char *argv[])

 

{

 

 

 

 

X a, b, *pb=&b;

 

 

int X:: * pXint = & X::visible;

 

pfcn pF=&X::print;

 

 

a.set(7); a.reset ();

 

 

b.set (51 ); b.reset ();

 

 

a.print();

a.*pXint +=3;

 

a.print();

cout << "\nb.visible =

" <<pb->*pXint;

(b.*pF) ( );

pF = &X::reset; (a.*pF)( );

a.print (); cout << endl;

 

system("PAUSE");

return EXIT_SUCCESS; }

Разбор программы showhide

 

typedef

void (X:: *pfcn) ( );

- здесь сказано, что pfcn является именем

типа указателя на член класса X,

базовый тип которого - функция без

аргументов,

возвращая

void.

Функции-члены X::print и X::reset

соответствуют этому базовому типу. int X::*pXint = &X::visible; pfcn pF = &X::print;

Здесь объявляются переменная pXint, которая будет указателем на член класса X, базовый тип которого - int.

Она инициализируется так, чтобы указывать на член X::visible. Указатель pF инициализируется указателем на функцию-член X::print.

a.+pXint +=1; Это то же самое, что и ++a.visible.

cout << “ \nb.visible. Вызов функции равносилен b.print ( ). pF=&X::reset; (a.*pF) ( ) ;

Указателю pF присвоен адрес X::reset. Вызов функции равносилен a.reset ( ) .

Лабораторная работа № 8 Тема: Указатели в языке С++

Цель: Изучить использование динамической памяти Задание №1.

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

60

2.Напишите программу, реализующую сортировку объектов через массив указателей на них. В качестве объектов могут быть использованы объекты класса Person (некоторый человек) или Car (автомобиль).

3.Используя указатели, напишите программу для перевода 80 символов

строки s2 в строку s3.

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

(а) int *px;

Создайте целое, на которое указывает px , имеющее значение 41.

(б) long *a; int n; cin>>n;

Создайте динамический массив из длинных целых, на который указывает а.

(с ) struct DemoC

{

int one; long two;

double three; };

DemoC *p;

 

Создайте узел, на который указывает p. Затем задайте поля {1, 550000,3.14} 5.. Конструктор класса DynamicInt использует оператор для динамического выделения целого и присваивания его адреса указателю pn. Открытые методы GetVal и SetVal сохраняют и извлекают данные из динамической памяти. class DynamicInt {

private: int *pn;

public:

//конструктор и конструктор копирования DynamicInt(int n=0);

DynamicInt(const DynamicInt &x); //деструктор ~DynamicInt(void);

//оператор присваивания

DynamicInt& operator=(const DynamicInt & x); //методы управления данными

int GetVal(void); //получить данное void SetVal(int n); //установить целое //оператор преобразования

operator int(void); //возвращает целое //потоковый ввод/вывод

friend ostream& operator<< (ostream& ostr, const DynamicInt& x); friend istream& operator>> (istream& istr, DynamicInt& x); };

Реализуйте этот класс.

6. Используйте класс DynamicInt для следующих упражнений: DynamicInt *p;

61

Задайте объявление для выделения одного объекта с начальным значением. DynamicInt *p;

Выделите массив p с тремя элементами. Каково значение каждого объекта в массиве?

DynamicInt a[10];

Укажите, как объявить массив из 7 объектов типа DynamicInt и инициализировать каждый объект значением 110?

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

Контрольные вопросы

Указатель на void может содержать указатель на _________- .

Звездочка, расположенная после типа данных , означает ________

Звездочка, расположенная перед именем переменной, означает ______ .

Для чего предназначен конструктор копирования?

Каково значение ключевого слова this?

Назовите основное применение ключевого слова this?

Задание для самостоятельной работы

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

#include <assert.h>

//тип динамической матрицы class matrix {

private :

int c_size, r_size; double **p;

public :

matrix (int c, int r); ~matrix ( );

double & operator ( ) ( int i, int j ) ( return (p [ i ] [j ] ); } matrix & operator = (const matrix & m );

matrix & operator += (matrix & m ); };

matrix :: matrix ( int c , int r ) : c_size ( c ), r_size ( r ) { p = new double * [ c ];

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

p [ i ] = new double [ r ]; }

Определите все члены класса. Напишите программу для тестирования класса matrix.

62

Глава 9. Исключения

Классическое исключение - непредвиденное условие, сталкиваясь с которым программа не может с ним справиться. В коде С++ допускается непосредственно устанавливать исключение в try блоке, используя выражение throw. Исключение будет обрабатываться с помощью вызова соответствующего обработчика, выбираемого из списка, находящегося в try блоке. Например:

vect::vect (int n) {

// отказоустойчивый конструктор try {

if (n< 1) throw (n); if (p= = 0)

throw (“Free store exrausted”); }

catch (int n) { // обрабатывает неправильный размер } catch (const char * error)

{ //обрабатывает превышение объема свободной памяти } Синтаксически, выражения установки исключений проявляются в двух

формах: throw

throw выражение

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

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

Выражение

захватывается обработчиком, который может использовать его

значение.

 

 

void func( )

{

int i;

 

 

// какие-то операторы

throw i;

}

 

int main() {

 

try {

 

 

func();

}

catch (int n)

{ // } }

 

 

63

Значение целого числа, установленное throw i сохраняется до тех пор, пока не завершается обработчик с сигнатурой целого числа catch (int n). Это значение доступно для использования внутри обработчика в качестве его аргумента. Пример повторной установки исключения:

catch ( int n ) {

//

 

throw;

// повторная установка

}

Если установленное выражение имело целый тип, повторно

установленное

исключение - тот же самый целый постоянный

объект,

который обрабатывается ближайшим подходящим для этого

типа

обработчиком.

 

 

Блоки проверки

Синтаксически блок проверки имеет форму: try

составной оператор список обработчиков

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

try {

//

throw (“ABC”);

//

io_condition eof(argv[i]); throw (eof);

//

}

catch (const char *) { // } catch (io_condition & x) { // }

Выражение throw соответствует аргументу catch, если оно:

точно соответствует ему;

является общедоступным базовым классом порожденного типа, который собственно устанавливается;

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

64

Ошибочным будет перечисление обработчиков в порядке, который предотвращает их вызов. Например,

catch (void *) // будет соответствовать любой char * catch (char *)

catch (BaseTypeError &) //будет вызываться всегда для // DerivedTypeError

catch (DerivedTypeError &)

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

Обработчики

Синтаксически обработчик имеет следующую форму: сatch (формальный параметр)

составной оператор

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

catch (char * message)

{ cerrr<<message<<endl; exit (1); }

catch (. . . ) //действие, которое нужно принять по умолчанию { cerr << “все прочие ошибки” << endl; }

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

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

Спецификация исключения

Синтаксически спецификация исключения представляет собой часть объявления функции и имеет форму:

заголовок функции throw ( список типов )

Список типов –это список типов, которые могут иметь выражение throw внутри функции. Если этот список пуст, то throw не будет выполняться функцией.

void func( ) throw (int, over_flow ); void func_noex (int a) throw ( );

65

С помощью спецификации показывают ожидаемые исключения.

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

class

ObjectError {

 

 

public:

 

 

 

ObjectError (аргументы);

//получение полезной информации

virtual void repair( )

 

 

{ cerr << “Message about error “ <<endl; abort (); }

};

class ObjectErrorS1: public ObjectError {

 

public:

 

 

 

ObjectErrorS1 (аргументы );

 

 

void

repair ( );

//переопределение, для того

чтобы обеспечить

//соответствующее восстановление Эти иерархии позволяют соответственно упорядоченному множеству

catch, обрабатывать исключения в логической последовательности. Тип базового класса в списке объявлений catch должен следовать после типа порожденного класса.

Подтверждения

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

void assert (int expression);

Если выражение int expression оценивается как неправильное, тогда выполнение прерывается с выводом диагностики. Например, в конструкторе класса vect выполняется проверка распределения массива:

vect::vect ( int n) { if (n <=1 )

cerr << “ошибочный размер массива” << n << endl; exit (1 ); }

size = n;

p= new int [size ]; }

Можно заменить проверку на использование assert (

vect :: vect (int n ) { assert (n > 0 ); // оговоренное предусловие size = n ; p = new int [ size ];

assert ( p != 0); } //оговоренное постусловие

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

66

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

Лабораторная работа № 9 Тема: Обработка исключений

Цель: Изучить использование обработки исключений Задание

1

1.Объясните две причины, по которым объявление исключения в catchобработчике следует сделать ссылкой. Подтвердите примером программы.

2.Завершите класс Ptr_to_T ( Л1 стр 340)

class Ptr_to_T { T* p;

T* array; int size;

public:

Ptr_to_T(T* p, T* v, int s);

//связать с массивом v размера s и начальным значением p

Ptr_to_T(T* p); //связать с единственным объектом, начальное значение p Ptr_to_T& operator++(); //префиксный

Ptr_to_T& operator++(int);//постфиксный Ptr_to_T& operator--(); //префиксный Ptr_to_T& operator--(int); //постфиксный

T& operator* (); };

и протестируйте его. Чтобы быть полным, Ptr_to_T должен, по крайней мере, иметь операторы *, ->, =, ++, --. Сделайте так, чтобы ошибка на этапе выполнения не возникала до того как реально производится разыименование «висячей» ссылки.

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

№2

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

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

67

класса исключений, хранящего номер ошибки и ее название. Включите в класс конструктор.

Контрольные вопросы

1.Какие исключения может возбуждать функция, если ее спецификация исключений имеет вид throw() ?

2.Какое из следующих присваиваний ошибочно? Почему?

void example( ) throw( string );

(a)void (*pf1) ( )=example;

(b)void (*pf2) ( ) throw() = example;

Задание для самостоятельной работы

1.Следующая сортировка методом “пузырька“ работает неправильно.

//Неправильная сортировка методом “пузырька “

#include <iostream> using namespace std; void swap (int a, int b) { int temp=a;

a=b; b=temp; }

void bubble(int a[], int size) { int i, j;

for ( i=0; i!=size; ++i )

for (j=i ; j != size ; ++ j) if (a[j] < a [j+ 1]

swap ( a [j ] , a [j + 1] );

};

main () {

int t [10 ]= { 9,4,6,4, 5, 9, -3, 1, 0, 12}; bubble (t, 10);

for ( int i=0; i<10; ++i ) cout << t [ i ] << ‘\t’;

cout<< “\nsorted?

“<<endl; }

Поместите в этот код

подтверждения для того, чтобы убедиться, что он

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

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

68

Глава 10. Потоковый ввод-вывод

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

последовательность символов и наоборот. Операции

ввода/вывода

выполняются с помощью классов istream (потоковый

ввод) и ostream

(потоковый вывод). Третий класс, iostream, является производным от них и поддерживает двунаправленный ввод/вывод. Для удобства в библиотеке определены три стандартных объекта-потока:

cin – объект класса istream, соответствующий стандартному вводу. В общем случае он позволяет читать данные с терминала пользователя;

cout – объект класса ostream, соответствующий стандартному выводу. В общем случае он позволяет выводить данные на терминал пользователя;

cerr – объект класса ostream, соответствующий стандартному выводу для ошибок. В этот поток мы направляем сообщения об ошибках программы.

Вывод встроенных типов

Для управления выводом встроенных типов определяется класс ostream

с операцией << (вывести):

 

class ostream : public virtual ios {

 

// ...

 

public:

 

ostream& operator<<(const char*);

//строки

ostream& operator<<(char);

 

{ return *this << int(i); }

 

ostream& operator<<(int);

 

ostream& operator<<(double);

 

ostream& operator<<(const void*);

// указатели

// ...};

 

Функция operator<< возвращает ссылку на класс ostream, из которого она вызывалась, чтобы к ней можно было применить еще раз operator<<. Так, если х типа int, то cerr << "x = " << x; понимается как

(cerr.operator<<("x = ")).operator<<(x);

В частности, это означает, что если несколько объектов выводятся с помощью одного оператора вывода, то они будут выдаваться в естественном порядке: слева - направо. Функция ostream::operator<<(const void*) напечатает значение указателя в такой записи, которая более подходит для используемой системы адресации. Пример.

69

Соседние файлы в предмете [НЕСОРТИРОВАННОЕ]