Программирование на языке С++ с использованием классов.
Цель работы: 1) изучить возможности программирования классов на языке С++; 2) получить основные навыки программирования манипуляторов ввода/вывода.
Класс есть расширение понятия структуры языка С++. Он позволяет создавать типы и определять функции, которые задают поведение типа. Каждый представитель класса называется объектом.
Определение класса идентично определению структуры в С++, за исключением того, что
оно обычно содержит одну или несколько спецификаций доступа (public, protected, private);
вместо ключевого слова struct используется слово class;
оно обычно включает в себя функции (функции-элементы или методы) наряду с данными-элементами;
обычно в нем имеются некоторые специальные функции, такие как конструктор (функция с тем же именем, что и сам класс) и деструктор (функция, именем которой является имя класса с префиксом - тильдой (~)).
Пример 1. Определение класса.
class str { char *s;//элемент-данное public://спецификатор открытого доступа str(char *word);//функция-элемент: конструктор ~str();//функция-элемент: деструктор void write();//функция-элемент: метод печати };
В С++ можно ограничить видимость данных и функций класса при помощи меток public, protected, private. Метка-спецификатор доступа применяется ко всем элементам класса, следующим за ней, пока не встретится другая метка или кончится определение класса.
Метка-спецификатор public (открытый) используется тогда, когда элементы-данные и функции-элементы класса должны быть доступны для функций-элементов и других функций программы, в которой имеется представитель класса.
Метка-спецификатор protected (защищенный) используется в том случае, когда элементы данных и функции-элементы должны быть доступны для функций-элементов данного класса и классов производных от него.
Метка-спецификатор private (закрытый) используется, если элементы-данные и функции-элементы должны быть доступны только для функций-элементов данного класса.
В классе элементы по умолчанию являются закрытыми.
Элементы класса делятся на две основные категории:
данные, называемые элементами-данными;
код, называемый элементами-функциями или методами.
Данные-элементы классов С++ идентичны элементам структур языка С++ с некоторыми дополнениями:
данными-элементами могут быть перечислимые типы, битовые поля или представители ранее объявленного класса. Также допускается вложенное объявление перечислимого типа данных и создание псевдонимов с помощью typedef;
данное-элемент класса может быть указателем или ссылкой на представитель этого класса.
Функция-элемент является функцией, объявленной (описанной) внутри определения класса. Тело функции может также определяться внутри определения класса, в этом случае функция называется встроенной (inline) функцией-элементом. Когда тело функции определяется вне тела класса, перед именем функции ставится префикс из имени класса и операции разрешения видимости (::).
Пример2.
class str { char *s;// указатель на строку public: str(char *word) { // встроенный конструктор s=new char[strlen(word)+1]; strcpy(s, word); }; ~str() { // встроенный деструктор delete[] s; }; void write();// объявление функции-элемента }; void str::write() { // определение функции-элемента cout << s; };
Функции-элементы находятся в области действия класса, в котором они определены. Т.о. они могут обращаться к любому элементу класса, используя просто имя переменной. Обычные функции или функции-элементы другого класса могут получить доступ к элементам-данным с помощью операции . или >, применяемых к представителю или указателю на представитель класса.
Пример 3.
class coord { public: int x, y;// координаты x и y }; int main() { coord org;// представитель класса координат coord *orgptr = &org;// указатель на представитель класса org.x = 0;// задание значения координаты x orgptr->y = 0;// задание значения координаты y }
Функции-элементы класса могут вызывать другие функции-элементы того же класса, используя имя функции.
Пример 4.
class coord { int x, y;// координаты x и y public: void setcoord(int _x, int _y) // функция задания значений координат { x =_x; y =_y; }; void getcoord(int &_x, int &_y) //функция получения значений координат {_x = x; _y = y;}; }; int main() { coord org;// представитель класса координат coord *orgptr = &org;// указатель на представитель класса org.setcoord(10, 10);// вызов функции-элемента // задания значений int col, row; orgptr->getcoord(col, row);// вызов функции-элемента // получения значений координат }
Указатель this
Каждая нестатическая (не имеющая спецификатора static) функция-элемент имеет доступ к объекту, для которого вызвана, через ключевое слово this. Указатель this является указателем на тип_класса*.
Пример 5.
class simple { public: simple(); void greet() { cout<<" Hello!";}; }; simple::simple() { greet();// вызов this->greet(); // функции (*this).greet(); // greet() }
Т.к. функции-элементы могут обращаться ко всем элементам класса просто по имени, в основном указатель this используется для возвращения указателя (return this) или ссылки (return *this) на подразумеваемый объект.
Конструктор инициализирует представитель класса (объект) и является функцией-элементом с тем же именем, что и класс. Конструктор вызывается компилятором всегда, когда создается представитель класса. Объект считается созданным в тот момент, когда завершил работу конструктор объекта.
Для конструкторов выполняются следующие правила:
для конструктора не указывается возвращаемый тип;
конструктор не может возвращать значение;
конструктор не наследуется;
для одного класса может существовать один или несколько конструкторов;
если конструктор не задан явным образом, то автоматически создаётся пустой конструктор.
Деструктор является дополнением конструктора. Он имеет то же имя, что и класс, но с префиксом - тильдой (~). Он вызывается всякий раз, когда уничтожается представитель класса. Объект считается уничтоженным, когда завершил работу деструктор объекта. Для деструктора существуют следующие правила:
деструктор не может иметь аргументов;
деструктор не может возвращать значения;
деструктор не наследуется (исключением является виртуальный деструктор);
для одного класса может существовать только один деструктор;
если деструктор не задан явным образом, то автоматически создаётся пустой деструктор.
Пример 6.
file ctime.h
#ifndef __CTIME_H__ #define __CTIME_H__ class CTime {char *timestr; public: CTime(char *str="00:00:00");//конструктор по умолчанию CTime(const CTime& clk);//копирующий конструктор ~CTime();//деструктор show();//функция-элемент };//обязательно ставить точку с запятой, т.к. class – // объявление типа #endif
file ctime.cpp
#include <string.h> #include <iostream.h> #include "ctime.h" CTime::CTime(char *str="00:00:00") { timestr=new char[strlen(str)+1]; strcpy(timestr,str); } CTime:: CTime(const time& clk) { timestr=new char[strlen(clk.timestr)+1]; strcpy(timestr,clk.timestr); } CTime::~ CTime() { delete [] timestr; } CTime::show() { std::cout << "Time is " << timestr << std::endl; }
file main.cpp
#include "ctime.h" int main(void) { CTime a;//для а вызывается конструктор по умолчанию CTime *b=new CTime;//для b вызывается конструктор по умолчанию CTime e(a);//для e вызывается копирующий конструктор //вызовем функцию-элемент a.show();//00:00:00 b->show();//00:00:00 e.show;//00:00:00 }
в конце области видимости автоматически вызываются деструкторы объектов в порядке, обратном вызову конструкторов, т.е. сначала для е, затем для d и т.д..
При вводе/выводе данных можно воспользоваться манипуляторами, то есть специальными функциями форматирования, которые могут находиться в теле оператора ввода/вывода. Если в манипуляторе используются параметры, то необходимо подключение заголовочного файла <iomanip.h>.
Для сохранения и восстановления состояния потока используется функция-метод класса потока flags(). Например:
long a;
a=cout.flags();//для сохранения состояния потока в а
cout.flags(a);//для восстановления состояния потока из а
Манипулятор | Назначение | Ввод/вывод |
---|---|---|
dec | Вывод числовых данных в десятичной системе счисления. | Вывод |
hex | Вывод числовых данных в шестнадцатеричной системе счисления. | Вывод |
oct | Вывод числовых данных в восьмеричной системе счисления. | Вывод |
endl | Вывод символа новой строки и флэширование. | Вывод |
ends | Вывод нуля (NULL). | Вывод |
flush | Сброс буферизованового вывода на диск. | Вывод |
ws | Пропуск начальных пробелов. | Ввод |
resetiosflags(long f) | Сброс флагов, задаваемых в f. | Ввод/вывод |
setbase(int base) | Устанавливает основание системы счисления для вывода данных. | Вывод |
setfill(char ch) | Устанавливает символ заполнения ch. | Вывод |
setiosflags(long f) | Установка флагов, задаваемых в f. | Вывод |
setprecision(int p) | Задает число символов после десятичной точки, равным p. | Вывод |
setw(int w) | Задает ширину поля, равной w позиций. | Вывод |
Пример 7. Вывод данных с использованием манипуляторов.
#include <iostream> #include <iomanip> #include <cmath> int main() { double x, y; std::cout << "Input x "; std::cin >> x; y = sin(x); std::cout << std::setprecision(3); std::cout << std::setw(7) << x; std::cout << std::setw(7) << y; return 0; }
Пример 8. Описать и определить класс-список.
Файл list.h содержит описание класса.
#pragma once struct list { int inf;// информационное поле list *next;// указатель на следующий элемент списка }; class CSpisok { list* head;// указатель на начало списка public: CSpisok (int); CSpisok (CSpisok&); ~CSpisok(); void print (); };
Файл list.cpp содержит определение функций-элементов.
#include <cstdlib> #include <iostream> #include <iomanip> #include "list.h" //конструктор инициализирует список из n элементов по принципу "очередь" CSpisok:: CSpisok(int n) { head = NULL; list *p,*pn; for (int i = 0; i < n; i++) { p = new list; p->inf = random(100)-50; p->next = NULL; if (head == NULL) head = p; else pn->next = p; pn = p; } } //конструктор копии класса CSpisok CSpisok:: CSpisok (const CSpisok& s) { head = NULL; list *sp = s.head, *p, *pn; while (sp) { p = new list; p->inf = sp->inf; p->next = NULL; if (head == NULL) head = p; else pn->next = p; pn = p; sp = sp->next; } } //деструктор - уничтожает объект класса список из памяти CSpisok::~CSpisok() { list *p; while (head) { p = head; head = head->next; delete p; } } //функция-элемент печати содержимого списка void CSpisok::print() { list *p = head; while (p) { std::cout << std::setw(5) << p->inf; p = p->next; } std::cout << std::endl; }
Файл main.cpp содержит основную функцию.
#include <iostream> #include <iomanip> #include "list.h" int main() { spisok s1(10), // создание списка из 10 элементов s2(s1), // s2- копия списка s1 s3(15); // создание списка из 15 элементов s1.print(); // печать s1 s2.print(); // печать s2 s3.print(); // печать s3 }
В проект включены файлы: main.cpp и list.cpp.
Результаты выполнения программы:
-49 -50 -17 -47 -15 -29 3 -31 20 44 -49 -50 -17 -47 -15 -29 3 -31 20 44 -23 -6 -40 19 6 -46 -34 31 18 26 32 45 -29 -8 45