Лабораторная работа №2-3

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

Цель:

- познакомиться с созданием конструкторов и деструкторов;

- научиться создавать программы на С++ с использованием указателей и ссылок;

- уметь использовать конструкторы и деструкторы.

Краткая теория.

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

Объявление класса, дополненное конструкторами, может иметь следующий вид:

class Simple_Counter {

int count;

public:

// конструктор без параметров

Simple_Counter() { count = 0; }

// конструктор с параметром

Simple_Counter(int beg_val) { count = beg_val; }

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

Simple_Counter(Simple_Counter & old_obj) { count = old_obj.count; }

void SetCount(int val) { count = val; }

int GetCount() { return count; }

};

void main() {

// вызывается конструктор без параметров

Simple_Counter my_count;

// ...

// вызывается конструктор с параметром

Simple_Counter * pmy_count = new Simple_Counter(66);

// ...

// вызывается конструктор копирования

Simple_Counter your_count = Simple_Counter(my_counter);

// ...

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

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

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

В Си++ возможно определение массива объектов класса. При этом конструктор и деструктор вызываются для каждого элемента массива и не должны иметь параметров. При выполнении оператора delete для ссылки на массив объектов необходимо также указывать его размерность. Конструктор для массива объектов должен быть без параметров.

//---------------------------------

#include <stdio.h>

#include <dos.h>

static int days[] = { 0,31,28,31,30,31,30,31,31,30,31,30,31 };

class dat {

int day,month,year;

public:

dat(int,int,int); // Конструктор с параметрами (возможно умолчание)

dat(char *); // Конструктор с одним параметром

dat(); // Конструктор без параметров

~dat(); // Деструктор

};

//------- Конструктор с параметром - текстовая строка ---------

dat::dat(char *s) {

int i;

char ss[80];

strcpy(ss,s);

for (i=0; ss[i] !=0; i++)

if (ss[i]=='-') ss[i]=','; // Замена '-' на ','

sscanf(ss,"%d%d%d",&day,&month,&year);

}

// Конструктор с тремя параметрами (по умолчанию 0 - текущая дата)

dat::dat(int d=0, int m=0, int y=0) {

struct date x;

getdate(&x); // Стандартная функция получения текущей даты

// Проверка на значение по умолчанию

year = (y == 0) ? x.da_year : y;

month= (m == 0) ? x.da_month: m;

day = (d == 0) ? x.da_day : d;

}

//------ Конструктор без параметров --------------------------

dat::dat() {

struct date x;

getdate(&x); // Стандартная функция получения текущей даты

year = x.da_year ;

month= x.da_month;

day = x.da_day ;

}

//------ Деструктор ------------------------------------------

dat::~dat() {

printf("Дата ==> %2d-%2d-%4d\n",day,month,year);

}

//-----------------------------------------------

dat a("12-12-1990");

// Внешняя переменная - конструктор вызывается перед main()

dat b[10];

// Массив объектов - конструктор без параметров вызывается перед main()

void xxx(dat &p) {

dat c(12,12); // Вызывается Конструктор dat(int,int,int)

// для автоматического объекта

dat d = p; // Конструктор для автоматического объекта не

... // вызывается, т.к. объект инициализируется копированием

} // При выходе из функции вызываются деструкторы для объектов c и d

void main() {

int i,n;

scanf("%d",&n);

dat *p = new dat[n]; // Создание массива динамических объектов

// конструктор без параметров явно вызывается

for (i=0; i<10; i++) // n раз

xxx(b[i]); // При вызове неявно передается ссылка на b[i]

for (i=0; i< n; i++) //

xxx(p[i]) // При вызове неявно передается ссылка на p[i]

delete[n] p; // Уничтожение массива динамических объектов

// деструктор явно вызывается n раз

} // Деструктры для a и b[10] вызываются после

// выхода из main()

}

Практические задания:

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

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