Файл: Контрольная работа классы и объекты в с Основное содержание работы.docx
ВУЗ: Не указан
Категория: Не указан
Дисциплина: Не указана
Добавлен: 09.01.2024
Просмотров: 24
Скачиваний: 2
ВНИМАНИЕ! Если данный файл нарушает Ваши авторские права, то обязательно сообщите нам.
КОНТРОЛЬНАЯ РАБОТА
КЛАССЫ И ОБЪЕКТЫ В С++
Основное содержание работы.
Написать программу, в которой создаются и разрушаются объекты, определенного пользователем класса. Выполнить исследование вызовов конструкторов и деструкторов, а также отдельных методов классов.
Краткие теоретические сведения.
Класс.
Класс фундаментальное понятие С++, он лежит в основе многих свойств С++. Класс предоставляет механизм для создания программных объектов, в котором неразрывно объединены важнейшие концепции объектно-ориентированного программирования: инкапсуляция, наследование, полиморфизм. С точки зрения синтаксиса, класс в С++ это структурированный тип, образованный на основе уже существующих типов. В этом смысле класс является расширением понятия структуры. В простейшем случае класс можно определить с помощью конструкции:
тип_класса имя_класса{список_членов_класса};
где
тип_класса – одно из служебных слов class, struct;
имя_класса – идентификатор;
список_членов_класса – определения и описания, типизированных данных и принадлежащих классу функций.
Функции – это методы класса, определяющие операции над объектом.
Данные – это поля объекта, образующие его структуру. Значения полей определяет состояние объекта.
Примеры.
struct date // дата
{int month,day,year; // поля: месяц, день, год
void set(int,int,int); // метод – установить дату
void get(int*,int*,int*); // метод – получить дату
void next(); // метод – установить следующую дату
void print(); // метод – вывести дату
};
struct class complex // комплексное число
{double re,im;
double real(){return(re);}
double imag(){return(im);}
void set(double x,double y){re = x; im = y;}
void print(){cout<<“re = “<
};
Для описания объекта класса (экземпляра класса) используется конструкция
имя_класса имя_объекта;
date today,my_birthday;
date *point = &today; // указатель на объект типа date
date clim[30]; // массив объектов
date &name = my_birthday; // ссылка на объект
В определяемые объекты входят данные, соответствующие членам данным класса. Функции члены класса позволяют обрабатывать данные конкретных объектов класса. Обращаться к данным объекта и вызывать функции для объекта можно двумя способами. Первый с помощью “квалифицированных” имен:
имя_объекта. имя_данного
имя_объекта. имя_функции
Например:
complex x1,x2;
x1.re = 1.24;
x1.im = 2.3;
x2.set(5.1,1.7);
x1.print();
Второй способ доступа использует указатель на объект
указатель_на_объект–>имя_компонента
complex *point = &x1; // или point = new complex;
point –>re = 1.24;
point –>im = 2.3;
point –>print();
Доступность компонентов класса.
В рассмотренных ранее примерах классов компоненты классов являются общедоступными. В любом месте программы, где “видно” определение класса, можно получить доступ к компонентам объекта класса. Тем самым не выполняется основной принцип абстракции данных – инкапсуляция (сокрытие) данных внутри объекта. Для изменения видимости компонент в определении класса можно использовать спецификаторы доступа: public, private, protected.
Общедоступные (public) компоненты доступны в любой части программы. Они могут использоваться любой функцией как внутри данного класса, так и вне его. Доступ извне осуществляется через имя объекта:
имя_объекта.имя_члена_класса
ссылка_на_объект.имя_члена_класса
указатель_на_объект->имя_члена_класса
Собственные (private) компоненты локализованы в классе и не доступны извне. Они могут использоваться функциями – членами данного класса и функциями – “друзьями” того класса, в котором они описаны. Защищенные (protected) компоненты доступны внутри класса и в производных классах.
Изменить статус доступа к компонентам класса можно и с помощью использования в определении класса ключевого слова class. В этом случае все компоненты класса по умолчанию являются собственными.
Пример.
class complex
{
double re, im; // private по умолчанию
public:
double real(){return re;}
double imag(){return im;}
void set(double x,double y){re = x; im = y;}
};
Конструктор.
Для каждого вновь создаваемого объекта необходимо вызвать функцию типа set (как для класса complex) либо явным образом присваивать значения данным объекта. Для инициализации объектов класса в его определение включена специальная функция, называемая конструктором. Формат определения конструктора следующий:
имя_класса(список_форм_параметров){операторы_тела_конструктора}
Имя этой функции по правилам языка С++ должно совпадать с именем класса. Такая функция автоматически вызывается при определении или размещении в памяти с помощью оператора new каждого объекта класса.
Пример.
сomplex(double re1 = 0.0,double im1 = 0.0){re = re1; im = im1;}
Конструктор выделяет память для объекта и инициализирует данные члены класса.
Конструктор имеет ряд особенностей:
-
Для конструктора не определяется тип возвращаемого значения. Даже тип void не допустим.
-
Указатель на конструктор не может быть определен, и соответственно нельзя получить адрес конструктора.
-
Конструкторы не наследуются.
-
Конструкторы не могут быть описаны с ключевыми словами virtual, static, const, mutuable, valatile.
Конструктор всегда существует для любого класса, причем, если он не определен явно, он создается автоматически. По умолчанию создается конструктор без параметров и конструктор копирования. Если конструктор описан явно, то конструктор по умолчанию не создается. По умолчанию конструкторы создаются общедоступными (public).
Параметром конструктора не может быть его собственный класс, но может быть ссылка на него (T&). Без явного указания программиста конструктор всегда автоматически вызывается при определении (создании) объекта. В этом случае вызывается конструктор без параметров. Для явного вызова конструктора используются две формы:
имя_класса имя_объекта (фактические_параметры);
имя_класса (фактические_параметры);
Первая форма допускается только при не пустом списке фактических параметров. Она предусматривает вызов конструктора при определении нового объекта данного класса:
complex ss (5.9,0.15);
Вторая форма вызова приводит к созданию объекта без имени:
complex ss = complex (5.9,0.15);
Существуют два способа инициализации данных объекта с помощью конструктора. Ранее мы рассматривали первый способ, а именно, передача значений параметров в тело конструктора. Второй способ предусматривает применение списка инициализаторов данного класса. Этот список помещается между списком параметров и телом конструктора. Каждый инициализатор списка относится к конкретному компоненту и имеет вид:
имя_данного (выражение)
Примеры.
class CLASS_A
{
int i; float e; char c;
public:
CLASS_A(int ii,float ee,char cc) : i(8),e( i * ee + ii ),с(сс){}
. . .
};
Класс “символьная строка”.
#include
#include
class string
{
char *ch; // указатель на текстовую строку
int len; // длина текстовой строки
public:
// конструкторы
// создает объект – пустая строка
string(int N = 80): len(0){ch = new char[N+1]; ch[0] = ‘\0’;}
// создает объект по заданной строке
string(const char *arch){len = strlen(arch);
ch = new char[len+1];
strcpy(ch,arch);}
// компоненты-функции
// возвращает ссылку на длину строки
int& len_str(void){return len;}
// возвращает указатель на строку
char *str(void){return ch;}
. . .};
Здесь у класса string два конструктора – перегружаемые функции.
По умолчанию создается также конструктор копирования вида T::T(const T&), где Т – имя класса. Конструктор копирования вызывается всякий раз, когда выполняется копирование объектов, принадлежащих классу. В частности он вызывается:
а) когда объект передается функции по значению;
б) при построении временного объекта как возвращаемого значения функции;
в) при использовании объекта для инициализации другого объекта.
Если класс не содержит явным образом определенного конструктора копирования, то при возникновении одной из этих трех ситуаций производится побитовое копирование объекта. Побитовое копирование не во всех случаях является адекватным. Именно для таких случаев и необходимо определить собственный конструктор копирования. Например, в классе string:
string(const string& st)
{len=strlen(st.len);
ch=new char[len+1];
strcpy(ch,st.ch); }
Можно создавать массив объектов, однако при этом соответствующий класс должен иметь конструктор по умолчанию (без параметров).
Массив объектов может инициализироваться либо автоматически конструктором по умолчанию, либо явным присваиванием значений каждому элементу массива.
class demo{
int x;
public:
demo(){x=0;}
demo(int i){x=i;}
};
void main(){
class demo a[20]; //вызов конструктора без параметров(по умолчанию)
class demo b[2]={demo(10),demo(100)};//явное присваивание
Деструктор.
Динамическое выделение памяти для объекта создает необходимость освобождения этой памяти при уничтожении объекта. Например, если объект формируется как локальный внутри блока, то целесообразно, чтобы при выходе из блока, когда уже объект перестает существовать, выделенная для него память была возвращена. Желательно, чтобы освобождение памяти происходило автоматически. Такую возможность обеспечивает специальный компонент класса – деструктор класса. Его формат:
имя_класса(){операторы_тела_деструктора}
Имя деструктора совпадает с именем его класса, но предваряется символом “
” (тильда).
Деструктор не имеет параметров и возвращаемого значения. Вызов деструктора выполняется не явно (автоматически), как только объект класса уничтожается.
Например, при выходе за область определения или при вызове оператора delete для указателя на объект.
string *p=new string “строка”);
delete p;
Если в классе деструктор не определен явно, то компилятор генерирует деструктор по умолчанию, который просто освобождает память, занятую данными объекта. В тех случаях, когда требуется выполнить освобождение и других объектов памяти, например область, на которую указывает ch в объекте string, необходимо определить деструктор явно:
string(){delete []ch;}
Так же, как и для конструктора, не может быть определен указатель на деструктор.
Указатели на компоненты-функции.
Можно определить указатель на компоненты-функции.
тип_возвр_значения(имя_класса::*имя_указателя_на_функцию) (специф_параметров_функции);
Пример.
double(complex : :*ptcom)(); // Определение указателя
ptcom = &complex : : real; // Настройка указателя
// Теперь для объекта А можно вызвать его функцию
complex A(5.2,2.7);
cout<<(A.*ptcom)();
Можно определить также тип указателя на функцию
typedef double&(complex::*PF)();
а затем определить и сам указатель
PF ptcom=&complex::real;
Порядок выполнения работы.
1. Определить пользовательский класс в соответствии с вариантом задания (смотри приложение).
2. Определить в классе следующие конструкторы: без параметров, с параметрами, копирования.
3. Определить в классе деструктор.
4. Определить в классе компоненты-функции для просмотра и установки полей данных.
5. Определить указатель на компоненту-функцию.
6. Определить указатель на экземпляр класса.
7. Написать демонстрационную программу, в которой создаются и разрушаются объекты пользовательского класса и каждый вызов конструктора и деструктора сопровождается выдачей соответствующего сообщения (какой объект, какой конструктор или деструктор вызвал).
8. Показать в программе использование указателя на объект и указателя на компоненту-функцию.
Методические указания.
1. Пример определения класса.
const int LNAME=25;
class STUDENT{
char name[LNAME]; // имя
int age; // возраст
float grade; // рейтинг
public:
STUDENT(); // конструктор без параметров
STUDENT(char*,int,float); // конструктор с параметрами
STUDENT(const STUDENT&); // конструктор копирования
STUDENT();
char * GetName() ;
int GetAge() const;
float GetGrade() const;
void SetName(char*);
void SetAge(int);
void SetGrade(float);
void Set(char*,int,float);
void Show(); };
Более профессионально определение поля name типа указатель: char* name. Однако в этом случае реализация компонентов-функций усложняется.
2. Пример реализации конструктора с выдачей сообщения.
STUDENT::STUDENT(char*NAME,int AGE,float GRADE)
{
strcpy(name,NAME); age=AGE; grade=GRADE;
cout<< \nКонструктор с параметрами вызван для объекта <
}
3. Следует предусмотреть в программе все возможные способы вызова конструктора копирования. Напоминаем, что конструктор копирования вызывается:
а) при использовании объекта для инициализации другого объекта
Пример.
STUDENT a(“Иванов”,19,50), b=a;
б) когда объект передается функции по значению
Пример.
void View(STUDENT a){a.Show;}
в) при построении временного объекта как возвращаемого значения функции
Пример.
STUDENT NoName(STUDENT & student)
{STUDENT temp(student);
temp.SetName(“NoName”);
return temp;}
STUDENT c=NoName(a);
4. В программе необходимо предусмотреть размещение объектов как в статической, так и в динамической памяти, а также создание массивов объектов.
Примеры.
а) массив студентов размещается в статической памяти
STUDENT gruppa[3];
gruppa[0].Set(“Иванов”,19,50);
и т.д.
или
STUDENT gruppa[3]={STUDENT(“Иванов”,19,50),
STUDENT(“Петрова”,18,25.5),
STUDENT(“Сидоров”,18,45.5)};
б) массив студентов размещается в динамической памяти
STUDENT *p;
p=new STUDENT [3];
p-> Set(“Иванов”,19,50);
и т.д.
5. Пример использования указателя на компонентную функцию
void (STUDENT::*pf)();
pf=&STUDENT::Show;
(p[1].*pf)();
6. Программа использует три файла:
-
заголовочный h-файл с определением класса,
-
cpp-файл с реализацией класса,
-
сpp-файл демонстрационной программы.
Для предотвращения многократного включения файла-заголовка следует использовать директивы препроцессора
#ifndef STUDENTH
#define STUDENTH
// модуль STUDENT.H
...
#endif
Вариант задания 15
БИБЛИОТЕКА
имя – char*
автор – char*
стоимость – float
Содержание отчета.
1. Титульный лист: название дисциплины; номер и наименование работы; фамилия, имя, отчество студента; дата выполнения.
2. Постановка задачи. Следует дать конкретную постановку, т.е. указать, какой класс должен быть реализован, какие должны быть в нем конструкторы, компоненты-функции и т.д.
3. Определение пользовательского класса с комментариями.
4. Реализация конструкторов и деструктора.
5. Фрагмент программы, показывающий использование указателя на объект и указателя на функцию с объяснением.
6. Листинг основной программы, в котором должно быть указано, в каком месте и какой конструктор или деструктор вызываются.
7. Все основные действия должны иметь соответствующий комментарий.
МИНИСТЕРСТВО НАУКИ И ВЫСШЕГО ОБРАЗОВАНИЯ РОССИЙСКОЙ ФЕДЕРАЦИИ Федеральное государственное бюджетное образовательное учреждение высшего образования
«ТОМСКИЙ ГОСУДАРСТВЕННЫЙ УНИВЕРСИТЕТ СИСТЕМ УПРАВЛЕНИЯ И РАДИОЭЛЕКТРОНИКИ» (ТУСУР)
Факультет систем управления
Дисциплина «Объектно-ориентированное программирование»
Кафедра АСУ
Группа З-444
Отчет по контрольной работе
Классы и объекты в C++
Задача №1
Студент
А А.А.
Преподаватель
д.т.н., профессор
Дата выполнения
18.06.2020
Томск-2020
Задача: разработать пользовательский класс, обрабатывающий информацию о рабочем (имя, номер цеха, разряд). Определить конструктор по умолчанию (без параметров), с параметрами, конструктор копирования. Определить деструктор. Определить указатели на экземпляр класса и компоненту-функцию. Написать демонстрационную программу.
Решение:
Интерфейс класса:
//интерфейс класса
…
class staff
{
public:
//кострукторы: по умолчанию, с параметрами, копирования
staff(void);
…
public:
//деструктор
private:
//поля класса
…
//константы
static const int size = 50;
static const int first = 1;
static const int six = 6;
};
Реализация класса:
//реализация класса
#include "StdAfx.h"
#include "staff.h"
staff::staff(void)
{
//инициализируем члены класса по умолчанию
…
this->bCategory = 0;
cout << "Базовый конструктор по умолчанию вызван The designer by default is caused."<< endl;
}
staff::staff(const char* const Name,…)
{
//инициализируем члены класса полученными переменными
…
int i = 0;
//копируем имя (кол-во символов ограничено size)
for(i=0; ipcName[i]=Name[i]), i++){}
//проверяем корректность данных
//и устанавливаем разряд с проверкой на корректность
if(Category<=six && Category>=first)
…
else
this->bCategory=first;
//устанавливаем номер цеха
this->iNumber = Number;
cout << "Вызван конструктор с параметрами The designer with parametres is caused."<< endl;
}
staff::staff(const staff &Copy)
{
//инициализируем переменные соответствующими значениями
//переменных полученного экземпляра класса
…
int i = 0;
//копируем имя (кол-во символов ограничено size)
…
this->bCategory = Copy.bCategory;
cout << "Вызван конструктор копирования The designer of coping is caused."<< endl;
}
void staff::setStaff(const char *const Name, const int Number, const int Category)
{
//устанавливаем все поля класса согласно полученным значениям
int i = 0;
//копируем имя (кол-во символов ограничено size)
for(i=0; ipcName[i]=Name[i]), i++){}
//проверяем корректность данных
//и устанавливаем разряд с проверкой на корректность
…
//устанавливаем номер цеха
this->iNumber = Number;
}
void staff::setName(const char* const Name)
{
//устанавливаем имя
…
//копируем имя (кол-во символов ограничено size)
for(i=0; ipcName[i]=Name[i]), i++){}
}
void staff::setCategory(const int Category)
{
//проверяем корректность данных и устанавливаем разряд
…
else
this->bCategory=first;
}
void staff::setNumber(const int Number)
{
//устанавливаем номер цеха
this->iNumber = Number;
}
char* staff::getName(void) const
{
//возвращаем имя
return this->pcName;
}
int staff::getNumber(void) const
{
//возвращаем номер цеха
return this->iNumber;
}
int staff::getCCategory(void) const
{
//возвращаем разряд
return this->bCategory;
}
void staff::print(void)
{
//выводим все поля на консоль
…
cout << "Категория: "<< this->bCategory<< endl;
}
staff::
staff(void)
КОНТРОЛЬНАЯ РАБОТА
КЛАССЫ И ОБЪЕКТЫ В С++
Основное содержание работы.
Написать программу, в которой создаются и разрушаются объекты, определенного пользователем класса. Выполнить исследование вызовов конструкторов и деструкторов, а также отдельных методов классов.
Краткие теоретические сведения.
Класс.
Класс фундаментальное понятие С++, он лежит в основе многих свойств С++. Класс предоставляет механизм для создания программных объектов, в котором неразрывно объединены важнейшие концепции объектно-ориентированного программирования: инкапсуляция, наследование, полиморфизм. С точки зрения синтаксиса, класс в С++ это структурированный тип, образованный на основе уже существующих типов. В этом смысле класс является расширением понятия структуры. В простейшем случае класс можно определить с помощью конструкции:
тип_класса имя_класса{список_членов_класса};
где
тип_класса – одно из служебных слов class, struct;
имя_класса – идентификатор;
список_членов_класса – определения и описания, типизированных данных и принадлежащих классу функций.
Функции – это методы класса, определяющие операции над объектом.
Данные – это поля объекта, образующие его структуру. Значения полей определяет состояние объекта.
Примеры.
struct date // дата
{int month,day,year; // поля: месяц, день, год
void set(int,int,int); // метод – установить дату
void get(int*,int*,int*); // метод – получить дату
void next(); // метод – установить следующую дату
void print(); // метод – вывести дату
};
struct class complex // комплексное число
{double re,im;
double real(){return(re);}
double imag(){return(im);}
void set(double x,double y){re = x; im = y;}
void print(){cout<<“re = “<
};
Для описания объекта класса (экземпляра класса) используется конструкция
имя_класса имя_объекта;
date today,my_birthday;
date *point = &today; // указатель на объект типа date
date clim[30]; // массив объектов
date &name = my_birthday; // ссылка на объект
В определяемые объекты входят данные, соответствующие членам данным класса. Функции члены класса позволяют обрабатывать данные конкретных объектов класса. Обращаться к данным объекта и вызывать функции для объекта можно двумя способами. Первый с помощью “квалифицированных” имен:
имя_объекта. имя_данного
имя_объекта. имя_функции
Например:
complex x1,x2;
x1.re = 1.24;
x1.im = 2.3;
x2.set(5.1,1.7);
x1.print();
Второй способ доступа использует указатель на объект
указатель_на_объект–>имя_компонента
complex *point = &x1; // или point = new complex;
point –>re = 1.24;
point –>im = 2.3;
point –>print();
Доступность компонентов класса.
В рассмотренных ранее примерах классов компоненты классов являются общедоступными. В любом месте программы, где “видно” определение класса, можно получить доступ к компонентам объекта класса. Тем самым не выполняется основной принцип абстракции данных – инкапсуляция (сокрытие) данных внутри объекта. Для изменения видимости компонент в определении класса можно использовать спецификаторы доступа: public, private, protected.
Общедоступные (public) компоненты доступны в любой части программы. Они могут использоваться любой функцией как внутри данного класса, так и вне его. Доступ извне осуществляется через имя объекта:
имя_объекта.имя_члена_класса
ссылка_на_объект.имя_члена_класса
указатель_на_объект->имя_члена_класса
Собственные (private) компоненты локализованы в классе и не доступны извне. Они могут использоваться функциями – членами данного класса и функциями – “друзьями” того класса, в котором они описаны. Защищенные (protected) компоненты доступны внутри класса и в производных классах.
Изменить статус доступа к компонентам класса можно и с помощью использования в определении класса ключевого слова class. В этом случае все компоненты класса по умолчанию являются собственными.
Пример.
class complex
{
double re, im; // private по умолчанию
public:
double real(){return re;}
double imag(){return im;}
void set(double x,double y){re = x; im = y;}
};
Конструктор.
Для каждого вновь создаваемого объекта необходимо вызвать функцию типа set (как для класса complex) либо явным образом присваивать значения данным объекта. Для инициализации объектов класса в его определение включена специальная функция, называемая конструктором. Формат определения конструктора следующий:
имя_класса(список_форм_параметров){операторы_тела_конструктора}
Имя этой функции по правилам языка С++ должно совпадать с именем класса. Такая функция автоматически вызывается при определении или размещении в памяти с помощью оператора new каждого объекта класса.
Пример.
сomplex(double re1 = 0.0,double im1 = 0.0){re = re1; im = im1;}
Конструктор выделяет память для объекта и инициализирует данные члены класса.
Конструктор имеет ряд особенностей:
-
Для конструктора не определяется тип возвращаемого значения. Даже тип void не допустим.
-
Указатель на конструктор не может быть определен, и соответственно нельзя получить адрес конструктора.
-
Конструкторы не наследуются.
-
Конструкторы не могут быть описаны с ключевыми словами virtual, static, const, mutuable, valatile.
Конструктор всегда существует для любого класса, причем, если он не определен явно, он создается автоматически. По умолчанию создается конструктор без параметров и конструктор копирования. Если конструктор описан явно, то конструктор по умолчанию не создается. По умолчанию конструкторы создаются общедоступными (public).
Параметром конструктора не может быть его собственный класс, но может быть ссылка на него (T&). Без явного указания программиста конструктор всегда автоматически вызывается при определении (создании) объекта. В этом случае вызывается конструктор без параметров. Для явного вызова конструктора используются две формы:
имя_класса имя_объекта (фактические_параметры);
имя_класса (фактические_параметры);
Первая форма допускается только при не пустом списке фактических параметров. Она предусматривает вызов конструктора при определении нового объекта данного класса:
complex ss (5.9,0.15);
Вторая форма вызова приводит к созданию объекта без имени:
complex ss = complex (5.9,0.15);
Существуют два способа инициализации данных объекта с помощью конструктора. Ранее мы рассматривали первый способ, а именно, передача значений параметров в тело конструктора. Второй способ предусматривает применение списка инициализаторов данного класса. Этот список помещается между списком параметров и телом конструктора. Каждый инициализатор списка относится к конкретному компоненту и имеет вид:
имя_данного (выражение)
Примеры.
class CLASS_A
{
int i; float e; char c;
public:
CLASS_A(int ii,float ee,char cc) : i(8),e( i * ee + ii ),с(сс){}
. . .
};
Класс “символьная строка”.
#include
#include
class string
{
char *ch; // указатель на текстовую строку
int len; // длина текстовой строки
public:
// конструкторы
// создает объект – пустая строка
string(int N = 80): len(0){ch = new char[N+1]; ch[0] = ‘\0’;}
// создает объект по заданной строке
string(const char *arch){len = strlen(arch);
ch = new char[len+1];
strcpy(ch,arch);}
// компоненты-функции
// возвращает ссылку на длину строки
int& len_str(void){return len;}
// возвращает указатель на строку
char *str(void){return ch;}
. . .};
Здесь у класса string два конструктора – перегружаемые функции.
По умолчанию создается также конструктор копирования вида T::T(const T&), где Т – имя класса. Конструктор копирования вызывается всякий раз, когда выполняется копирование объектов, принадлежащих классу. В частности он вызывается:
а) когда объект передается функции по значению;
б) при построении временного объекта как возвращаемого значения функции;
в) при использовании объекта для инициализации другого объекта.
Если класс не содержит явным образом определенного конструктора копирования, то при возникновении одной из этих трех ситуаций производится побитовое копирование объекта. Побитовое копирование не во всех случаях является адекватным. Именно для таких случаев и необходимо определить собственный конструктор копирования. Например, в классе string:
string(const string& st)
{len=strlen(st.len);
ch=new char[len+1];
strcpy(ch,st.ch); }
Можно создавать массив объектов, однако при этом соответствующий класс должен иметь конструктор по умолчанию (без параметров).
Массив объектов может инициализироваться либо автоматически конструктором по умолчанию, либо явным присваиванием значений каждому элементу массива.
class demo{
int x;
public:
demo(){x=0;}
demo(int i){x=i;}
};
void main(){
class demo a[20]; //вызов конструктора без параметров(по умолчанию)
class demo b[2]={demo(10),demo(100)};//явное присваивание
Деструктор.
Динамическое выделение памяти для объекта создает необходимость освобождения этой памяти при уничтожении объекта. Например, если объект формируется как локальный внутри блока, то целесообразно, чтобы при выходе из блока, когда уже объект перестает существовать, выделенная для него память была возвращена. Желательно, чтобы освобождение памяти происходило автоматически. Такую возможность обеспечивает специальный компонент класса – деструктор класса. Его формат:
имя_класса(){операторы_тела_деструктора}
Имя деструктора совпадает с именем его класса, но предваряется символом “
” (тильда).
Деструктор не имеет параметров и возвращаемого значения. Вызов деструктора выполняется не явно (автоматически), как только объект класса уничтожается.
Например, при выходе за область определения или при вызове оператора delete для указателя на объект.
string *p=new string “строка”);
delete p;
Если в классе деструктор не определен явно, то компилятор генерирует деструктор по умолчанию, который просто освобождает память, занятую данными объекта. В тех случаях, когда требуется выполнить освобождение и других объектов памяти, например область, на которую указывает ch в объекте string, необходимо определить деструктор явно:
string(){delete []ch;}
КОНТРОЛЬНАЯ РАБОТА
КЛАССЫ И ОБЪЕКТЫ В С++
Основное содержание работы.
Написать программу, в которой создаются и разрушаются объекты, определенного пользователем класса. Выполнить исследование вызовов конструкторов и деструкторов, а также отдельных методов классов.
Краткие теоретические сведения.
Класс.
Класс фундаментальное понятие С++, он лежит в основе многих свойств С++. Класс предоставляет механизм для создания программных объектов, в котором неразрывно объединены важнейшие концепции объектно-ориентированного программирования: инкапсуляция, наследование, полиморфизм. С точки зрения синтаксиса, класс в С++ это структурированный тип, образованный на основе уже существующих типов. В этом смысле класс является расширением понятия структуры. В простейшем случае класс можно определить с помощью конструкции:
тип_класса имя_класса{список_членов_класса};
где
тип_класса – одно из служебных слов class, struct;
имя_класса – идентификатор;
список_членов_класса – определения и описания, типизированных данных и принадлежащих классу функций.
Функции – это методы класса, определяющие операции над объектом.
Данные – это поля объекта, образующие его структуру. Значения полей определяет состояние объекта.
Примеры.
struct date // дата
{int month,day,year; // поля: месяц, день, год
void set(int,int,int); // метод – установить дату
void get(int*,int*,int*); // метод – получить дату
void next(); // метод – установить следующую дату
void print(); // метод – вывести дату
};
struct class complex // комплексное число
{double re,im;
double real(){return(re);}
double imag(){return(im);}
void set(double x,double y){re = x; im = y;}
void print(){cout<<“re = “<
};
Для описания объекта класса (экземпляра класса) используется конструкция
имя_класса имя_объекта;
date today,my_birthday;
date *point = &today; // указатель на объект типа date
date clim[30]; // массив объектов
date &name = my_birthday; // ссылка на объект
В определяемые объекты входят данные, соответствующие членам данным класса. Функции члены класса позволяют обрабатывать данные конкретных объектов класса. Обращаться к данным объекта и вызывать функции для объекта можно двумя способами. Первый с помощью “квалифицированных” имен:
имя_объекта. имя_данного
имя_объекта. имя_функции
Например:
complex x1,x2;
x1.re = 1.24;
x1.im = 2.3;
x2.set(5.1,1.7);
x1.print();
Второй способ доступа использует указатель на объект
указатель_на_объект–>имя_компонента
complex *point = &x1; // или point = new complex;
point –>re = 1.24;
point –>im = 2.3;
point –>print();
Доступность компонентов класса.
В рассмотренных ранее примерах классов компоненты классов являются общедоступными. В любом месте программы, где “видно” определение класса, можно получить доступ к компонентам объекта класса. Тем самым не выполняется основной принцип абстракции данных – инкапсуляция (сокрытие) данных внутри объекта. Для изменения видимости компонент в определении класса можно использовать спецификаторы доступа: public, private, protected.
Общедоступные (public) компоненты доступны в любой части программы. Они могут использоваться любой функцией как внутри данного класса, так и вне его. Доступ извне осуществляется через имя объекта:
имя_объекта.имя_члена_класса
ссылка_на_объект.имя_члена_класса
указатель_на_объект->имя_члена_класса
Собственные (private) компоненты локализованы в классе и не доступны извне. Они могут использоваться функциями – членами данного класса и функциями – “друзьями” того класса, в котором они описаны. Защищенные (protected) компоненты доступны внутри класса и в производных классах.
Изменить статус доступа к компонентам класса можно и с помощью использования в определении класса ключевого слова class. В этом случае все компоненты класса по умолчанию являются собственными.
Пример.
class complex
{
double re, im; // private по умолчанию
public:
double real(){return re;}
double imag(){return im;}
void set(double x,double y){re = x; im = y;}
};
Конструктор.
Для каждого вновь создаваемого объекта необходимо вызвать функцию типа set (как для класса complex) либо явным образом присваивать значения данным объекта. Для инициализации объектов класса в его определение включена специальная функция, называемая конструктором. Формат определения конструктора следующий:
имя_класса(список_форм_параметров){операторы_тела_конструктора}
Имя этой функции по правилам языка С++ должно совпадать с именем класса. Такая функция автоматически вызывается при определении или размещении в памяти с помощью оператора new каждого объекта класса.
Пример.
сomplex(double re1 = 0.0,double im1 = 0.0){re = re1; im = im1;}
Конструктор выделяет память для объекта и инициализирует данные члены класса.
Конструктор имеет ряд особенностей:
-
Для конструктора не определяется тип возвращаемого значения. Даже тип void не допустим.
-
Указатель на конструктор не может быть определен, и соответственно нельзя получить адрес конструктора.
-
Конструкторы не наследуются.
-
Конструкторы не могут быть описаны с ключевыми словами virtual, static, const, mutuable, valatile.
Конструктор всегда существует для любого класса, причем, если он не определен явно, он создается автоматически. По умолчанию создается конструктор без параметров и конструктор копирования. Если конструктор описан явно, то конструктор по умолчанию не создается. По умолчанию конструкторы создаются общедоступными (public).
Параметром конструктора не может быть его собственный класс, но может быть ссылка на него (T&). Без явного указания программиста конструктор всегда автоматически вызывается при определении (создании) объекта. В этом случае вызывается конструктор без параметров. Для явного вызова конструктора используются две формы:
имя_класса имя_объекта (фактические_параметры);
имя_класса (фактические_параметры);
Первая форма допускается только при не пустом списке фактических параметров. Она предусматривает вызов конструктора при определении нового объекта данного класса:
complex ss (5.9,0.15);
Вторая форма вызова приводит к созданию объекта без имени:
complex ss = complex (5.9,0.15);
Существуют два способа инициализации данных объекта с помощью конструктора. Ранее мы рассматривали первый способ, а именно, передача значений параметров в тело конструктора. Второй способ предусматривает применение списка инициализаторов данного класса. Этот список помещается между списком параметров и телом конструктора. Каждый инициализатор списка относится к конкретному компоненту и имеет вид:
имя_данного (выражение)
Примеры.
class CLASS_A
{
int i; float e; char c;
public:
CLASS_A(int ii,float ee,char cc) : i(8),e( i * ee + ii ),с(сс){}
. . .
};
Класс “символьная строка”.
#include
#include
class string
{
char *ch; // указатель на текстовую строку
int len; // длина текстовой строки
public:
// конструкторы
// создает объект – пустая строка
string(int N = 80): len(0){ch = new char[N+1]; ch[0] = ‘\0’;}
// создает объект по заданной строке
string(const char *arch){len = strlen(arch);
ch = new char[len+1];
strcpy(ch,arch);}
// компоненты-функции
// возвращает ссылку на длину строки
int& len_str(void){return len;}
// возвращает указатель на строку
char *str(void){return ch;}
. . .};
Здесь у класса string два конструктора – перегружаемые функции.
По умолчанию создается также конструктор копирования вида T::T(const T&), где Т – имя класса. Конструктор копирования вызывается всякий раз, когда выполняется копирование объектов, принадлежащих классу. В частности он вызывается:
а) когда объект передается функции по значению;
б) при построении временного объекта как возвращаемого значения функции;
в) при использовании объекта для инициализации другого объекта.
Если класс не содержит явным образом определенного конструктора копирования, то при возникновении одной из этих трех ситуаций производится побитовое копирование объекта. Побитовое копирование не во всех случаях является адекватным. Именно для таких случаев и необходимо определить собственный конструктор копирования. Например, в классе string:
string(const string& st)
{len=strlen(st.len);
ch=new char[len+1];
strcpy(ch,st.ch); }
Можно создавать массив объектов, однако при этом соответствующий класс должен иметь конструктор по умолчанию (без параметров).
Массив объектов может инициализироваться либо автоматически конструктором по умолчанию, либо явным присваиванием значений каждому элементу массива.
class demo{
int x;
public:
demo(){x=0;}
demo(int i){x=i;}
};
void main(){
class demo a[20]; //вызов конструктора без параметров(по умолчанию)
class demo b[2]={demo(10),demo(100)};//явное присваивание
Деструктор.
Динамическое выделение памяти для объекта создает необходимость освобождения этой памяти при уничтожении объекта. Например, если объект формируется как локальный внутри блока, то целесообразно, чтобы при выходе из блока, когда уже объект перестает существовать, выделенная для него память была возвращена. Желательно, чтобы освобождение памяти происходило автоматически. Такую возможность обеспечивает специальный компонент класса – деструктор класса. Его формат:
имя_класса(){операторы_тела_деструктора}Для конструктора не определяется тип возвращаемого значения. Даже тип void не допустим.
Указатель на конструктор не может быть определен, и соответственно нельзя получить адрес конструктора.
Конструкторы не наследуются.
Конструкторы не могут быть описаны с ключевыми словами virtual, static, const, mutuable, valatile.
#include
class string
{
char *ch; // указатель на текстовую строку
int len; // длина текстовой строки
public:
// конструкторы
// создает объект – пустая строка
string(int N = 80): len(0){ch = new char[N+1]; ch[0] = ‘\0’;}
// создает объект по заданной строке
string(const char *arch){len = strlen(arch);
ch = new char[len+1];
strcpy(ch,arch);}
// компоненты-функции
// возвращает ссылку на длину строки
int& len_str(void){return len;}
// возвращает указатель на строку
char *str(void){return ch;}
. . .};
Здесь у класса string два конструктора – перегружаемые функции.
По умолчанию создается также конструктор копирования вида T::T(const T&), где Т – имя класса. Конструктор копирования вызывается всякий раз, когда выполняется копирование объектов, принадлежащих классу. В частности он вызывается:
а) когда объект передается функции по значению;
б) при построении временного объекта как возвращаемого значения функции;
в) при использовании объекта для инициализации другого объекта.
Если класс не содержит явным образом определенного конструктора копирования, то при возникновении одной из этих трех ситуаций производится побитовое копирование объекта. Побитовое копирование не во всех случаях является адекватным. Именно для таких случаев и необходимо определить собственный конструктор копирования. Например, в классе string:
string(const string& st)
{len=strlen(st.len);
ch=new char[len+1];
strcpy(ch,st.ch); }
Можно создавать массив объектов, однако при этом соответствующий класс должен иметь конструктор по умолчанию (без параметров).
Массив объектов может инициализироваться либо автоматически конструктором по умолчанию, либо явным присваиванием значений каждому элементу массива.
class demo{
int x;
public:
demo(){x=0;}
demo(int i){x=i;}
};
void main(){
class demo a[20]; //вызов конструктора без параметров(по умолчанию)
class demo b[2]={demo(10),demo(100)};//явное присваивание
Деструктор.
Динамическое выделение памяти для объекта создает необходимость освобождения этой памяти при уничтожении объекта. Например, если объект формируется как локальный внутри блока, то целесообразно, чтобы при выходе из блока, когда уже объект перестает существовать, выделенная для него память была возвращена. Желательно, чтобы освобождение памяти происходило автоматически. Такую возможность обеспечивает специальный компонент класса – деструктор класса. Его формат:
Имя деструктора совпадает с именем его класса, но предваряется символом “
Так же, как и для конструктора, не может быть определен указатель на деструктор.
Указатели на компоненты-функции.
Можно определить указатель на компоненты-функции.
тип_возвр_значения(имя_класса::*имя_указателя_на_функцию) (специф_параметров_функции);
Пример.
double(complex : :*ptcom)(); // Определение указателя
ptcom = &complex : : real; // Настройка указателя
// Теперь для объекта А можно вызвать его функцию
complex A(5.2,2.7);
cout<<(A.*ptcom)();
Можно определить также тип указателя на функцию
typedef double&(complex::*PF)();
а затем определить и сам указатель
PF ptcom=&complex::real;
Порядок выполнения работы.
1. Определить пользовательский класс в соответствии с вариантом задания (смотри приложение).
2. Определить в классе следующие конструкторы: без параметров, с параметрами, копирования.
3. Определить в классе деструктор.
4. Определить в классе компоненты-функции для просмотра и установки полей данных.
5. Определить указатель на компоненту-функцию.
6. Определить указатель на экземпляр класса.
7. Написать демонстрационную программу, в которой создаются и разрушаются объекты пользовательского класса и каждый вызов конструктора и деструктора сопровождается выдачей соответствующего сообщения (какой объект, какой конструктор или деструктор вызвал).
8. Показать в программе использование указателя на объект и указателя на компоненту-функцию.
Методические указания.
1. Пример определения класса.
const int LNAME=25;
class STUDENT{
char name[LNAME]; // имя
int age; // возраст
float grade; // рейтинг
public:
STUDENT(); // конструктор без параметров
STUDENT(char*,int,float); // конструктор с параметрами
STUDENT(const STUDENT&); // конструктор копирования
заголовочный h-файл с определением класса,
cpp-файл с реализацией класса,
сpp-файл демонстрационной программы.
//проверяем корректность данных
//и устанавливаем разряд с проверкой на корректность
if(Category<=six && Category>=first)
…
else
this->bCategory=first;
//устанавливаем номер цеха
this->iNumber = Number;
cout << "Вызван конструктор с параметрами The designer with parametres is caused."<< endl;
}
staff::staff(const staff &Copy)
{
//инициализируем переменные соответствующими значениями
//переменных полученного экземпляра класса
…
int i = 0;
//копируем имя (кол-во символов ограничено size)
…
this->bCategory = Copy.bCategory;
cout << "Вызван конструктор копирования The designer of coping is caused."<< endl;
}
void staff::setStaff(const char *const Name, const int Number, const int Category)
{
//устанавливаем все поля класса согласно полученным значениям
int i = 0;
//копируем имя (кол-во символов ограничено size)
for(i=0; i
//проверяем корректность данных
//и устанавливаем разряд с проверкой на корректность
…
//устанавливаем номер цеха
this->iNumber = Number;
}
void staff::setName(const char* const Name)
{
//устанавливаем имя
…
//копируем имя (кол-во символов ограничено size)
for(i=0; i
}
void staff::setCategory(const int Category)
{
//проверяем корректность данных и устанавливаем разряд
…
else
this->bCategory=first;
}
void staff::setNumber(const int Number)
{
//устанавливаем номер цеха
this->iNumber = Number;
}
char* staff::getName(void) const
{
//возвращаем имя
return this->pcName;
}
int staff::getNumber(void) const
{
//возвращаем номер цеха
return this->iNumber;
}
int staff::getCCategory(void) const
{
//возвращаем разряд
return this->bCategory;
}
void staff::print(void)
{
//выводим все поля на консоль
…
cout << "Категория: "<< this->bCategory<< endl;
}
staff::
{
//очищаем память
delete [] pcName;
cout << "Деструктор вызван The destructor is caused."<< endl;
}
Демонстрационная программа:
#include "stdafx.h"
#include "staff.h"
//функция демонстрирует вызов конструктора копирования
//при передаче экземпляра класса по значению
void function(staff t){}
int main(int argc, char* argv[])
{
//создание экземпляра класса через указатель и оператор new
//конструктор по умолчанию
cout << "\nNumber1: ";
…
//создание экземпляров класса конструктор с параметрами
cout << "\nNumber2: ";
…
//конструктор копирования
cout << "\nNumber3: ";
staff number3(number2);
//демонстрация коструктора копирования при передачи по значению
cout << "\nFunction: ";
function(number3);
//демонстрация вызова методов
cout << "\nPrint number's: "<< endl;
number1->print();
…
//демонстрация установки полей
number1->setName("Nikolay");
//объвление указателя на компоненту-функцию
int(staff::*pGetCCategory)(void) const;
//присваивание указателю адреса метода
pGetCCategory = &staff::getCCategory;
//демонстрация использования указателя на компоненту-функцию
cout << "\nDemonstration point to method\nfor number1,2: "<< endl;
…
pGetCCategory = NULL;
//удаление экземпляра класса демонстрация деструктора
cout << "\nNumber1: ";
delete number1;
//демонстрация массива экземпляров класса
cout << "\nCreate massive on 6 elements:\n";
…
cout << "\nDeleting massive:\n";
delete [] massive;
cout << endl;
system("pause");
return EXIT_SUCCESS;
}
Пример выполнения программы:
Привести скриншот модулей (срр и h), а также запуска программы