- •Кетков ю.Л.
- •Раздел 5. Системные данные текстового типа 33
- •Раздел 6. Основные синтаксические конструкции языка c 46
- •Раздел 7. Указатели и ссылки 59
- •Раздел 8. Функции и их аргументы 62
- •Раздел 9. Работа с массивами. 74
- •Раздел 10. Пользовательские типы данных. 95
- •Раздел 11. Работа с файлами 104
- •Раздел 12. Библиотеки стандартных и нестандартных функций 118
- •Раздел 15. Классы. Создание новых типов данных 131
- •Раздел 16. Классы как средство создания больших программных комплексов 150
- •Раздел 17. Прерывания, события, обработка исключений 167
- •Введение
- •Раздел 1. Немного истории
- •Раздел 2. Структура программы на языке c
- •Раздел 3. Среда программирования
- •Раздел 4. Системные данные числового типа
- •4.1. Типы числовых данных и их представление в памяти эвм
- •4.1.1. Внутреннее представление целочисленных данных
- •4.1.2. Однобайтовые целочисленные данные
- •4.1.3. Двухбайтовые целочисленные данные
- •4.1.4. Четырехбайтовые целочисленные данные
- •4.1.5. Восьмибайтовые целочисленные данные
- •4.2. Внутреннее представление данных вещественного типа
- •4.3. Внешнее представление числовых констант
- •4.4. Объявление и инициализация числовых переменных
- •4.5. Ввод числовых данных по запросу программы
- •4.5.1. Потоковый ввод данных числового типа
- •4.5.2. Форматный ввод
- •4.6. Вывод числовых результатов
- •4.6.1. Форматный вывод
- •4.6.2. Потоковый вывод
- •4.7. Примеры программ вывода числовых данных
- •4.8. Операции над числовыми данными целого типа
- •4.9. Операции над числовыми данными вещественного типа
- •Раздел 5. Системные данные текстового типа
- •5.1. Символьные данные и их представление в памяти эвм
- •5.2. Строковые данные и их представление в памяти эвм
- •5.3. Ввод текстовых данных во время работы программы
- •5.3.1. Форматный ввод
- •5.3.3. Потоковый ввод
- •5.3.4. Специальные функции ввода текстовых данных
- •5.4. Вывод текстовых данных
- •5.4.1. Форматный вывод
- •5.5.2. Операции над строковыми данными
- •5.6. Управление дисплеем в текстовом режиме
- •Раздел 6. Основные синтаксические конструкции языка c
- •6.1. Заголовок функции и прототип функции
- •6.2. Объявление локальных и внешних данных
- •6.3. Оператор присваивания
- •6.4. Специальные формы оператора присваивания
- •6.5. Условный оператор
- •6.6. Оператор безусловного перехода
- •6.7. Операторы цикла
- •6.8. Дополнительные операторы управления циклом
- •6.9. Оператор выбора (переключатель)
- •6.10. Обращения к функциям
- •6.11. Комментарии в программах
- •Раздел 7. Указатели и ссылки
- •7.1. Объявление указателей
- •7.2. Операции над указателями
- •7.3. Ссылки
- •Раздел 8. Функции и их аргументы
- •8.1. Параметры-значения
- •8.2. Параметры-указатели
- •8.3. Параметры-ссылки
- •8.4. Параметры-константы
- •8.5. Параметры по умолчанию
- •8.6. Функции с переменным количеством аргументов
- •8.7. Локальные, глобальные и статические переменные
- •8.8. Возврат значения функции
- •8.9. Рекурсивные функции
- •8.10. Указатели на функцию и передача их в качестве параметров
- •8.11. "Левые" функции
- •Раздел 9. Работа с массивами.
- •9.1. Объявление и инициализация массивов.
- •9.2. Некоторые приемы обработки числовых массивов
- •9.2. Программирование задач линейной алгебры
- •9.2.1. Работа с векторами
- •9.2.2.Работа с матрицами
- •9.3. Поиск
- •9.3.1. Последовательный поиск
- •9.3.2. Двоичный поиск
- •9.4. Сортировка массивов.
- •9.4.1. Сортировка методом пузырька
- •9.4.2. Сортировка методом отбора
- •9.4.3. Сортировка методом вставки
- •9.4.4. Сортировка методом Шелла
- •9.4.5.Быстрая сортировка
- •9.5. Слияние отсортированных массивов
- •9.6. Динамические массивы.
- •Раздел 10. Пользовательские типы данных.
- •10.1. Структуры
- •10.1.1. Объявление и инициализация структур
- •10.1.2. Структуры – параметры функций
- •10.1.3.Функции, возвращающие структуры
- •10.2. Перечисления
- •10.3. Объединения
- •Раздел 11. Работа с файлами
- •11.1.Файлы в операционной системе
- •11.1. Текстовые (строковые) файлы
- •11.2. Двоичные файлы
- •11.3. Структурированные файлы
- •11.4. Форматные преобразования в оперативной памяти
- •11.5. Файловые процедуры в системе bcb
- •11.5.1. Проверка существования файла
- •11.5.2. Создание нового файла
- •11.5.3. Открытие существующего файла
- •11.5.4. Чтение из открытого файла
- •11.5.5. Запись в открытый файл
- •11.5.6. Перемещение указателя файла
- •11.5.7. Закрытие файла
- •11.5.8. Расчленение полной спецификации файла
- •11.5.9. Удаление файлов и пустых каталогов
- •11.5.10. Создание каталога
- •11.5.11. Переименование файла
- •11.5.12. Изменение расширения
- •11.5.13. Опрос атрибутов файла
- •11.5.14. Установка атрибутов файла
- •11.5.15. Опрос и изменение текущего каталога
- •11.6. Поиск файлов в каталогах
- •Раздел 12. Библиотеки стандартных и нестандартных функций
- •12.2. Организация пользовательских библиотек
- •12.3. Динамически загружаемые библиотеки
- •13.1. Препроцессор и условная компиляция
- •13.2. Компилятор bcc.Exe
- •13.3. Утилита grep.Com поиска в текстовых файлах
- •14.1. Переопределение (перегрузка) функций
- •14.2. Шаблоны функций
- •Раздел 15. Классы. Создание новых типов данных
- •15.1. Школьные дроби на базе структур
- •15.2. Школьные дроби на базе классов
- •15.3. Класс на базе объединения
- •15.4. Новые типы данных на базе перечисления
- •15.5. Встраиваемые функции
- •15.6. Переопределение операций (резюме)
- •15.8. Конструкторы и деструкторы (резюме)
- •Раздел 16. Классы как средство создания больших программных комплексов
- •16.1. Базовый и производный классы
- •16.1.1.Простое наследование
- •16.1.2. Вызов конструкторов и деструкторов при наследовании
- •16.1.3. Динамическое создание и удаление объектов
- •16.1.4. Виртуальные функции
- •16.1.5. Виртуальные деструкторы
- •16.1.6. Чистые виртуальные функции и абстрактные классы
- •16.2. Множественное наследование и виртуальные классы
- •16.3. Объектно-ориентированный подход к созданию графической системы
- •Раздел 17. Прерывания, события, обработка исключений
- •17.1. Аппаратные и программные прерывания
- •17.2. Исключения
15.2. Школьные дроби на базе классов
На первых порах развития языка C++ довольно часто прибегали к услугам структур для создания новых типов данных и процедур их обработки. Однако на современном этапе для создания классов обычно используют специальную конструкцию – class. На примере дробно-рациональных чисел попробуем разобраться в особенностях современного подхода. Ниже приводится текст файла rational.h, в котором содержится описание данных, конструкторов и некоторых методов для работы с дробями, а также прототипов всех используемых функций.
#ifndef __RATIONAL_H
#define __RATIONAL_H
//Класс Rational (У.Торп, У.Форд)
#include <iostream.h>
#include <stdlib.h>
//----------------------------------------------------------------
class Rational
{
private:
long num,den;
//Закрытый конструктор, используется в арифметических операциях
Rational(long num,long den);
//Функции-утилиты
void Reduce(void); //Метод – сокращение дроби
long gcd(long m,long n); //наибольший общий делитель
public:
Rational(int num=0, int denom=1); //Конструктор int->Rational
Rational(double x); //Конструктор double->Rational
//ввод/вывод
friend istream& operator>>(istream& t,Rational &r);
friend ostream& operator<<(ostream& t,const Rational &r);
// бинарные арифметические операции
Rational operator+(Rational r)const;
Rational operator-(Rational r)const;
Rational operator*(Rational r)const;
Rational operator/(Rational r)const;
//унарный минус, изменение знака
Rational operator-(void)const;
//операторы отношения
int operator<(Rational r)const;
int operator>(Rational r)const;
int operator==(Rational r)const;
//Преобразование Rational->double
operator double(void)const;
//Методы-утилиты
long GetNum(void)const;
long GetDen(void)const;
}; //конец объявления класса
#endif
Тела остальных функций и методов полезно вынести в отдельный файл с именем rational.cpp, содержимое которого приводится ниже:
#include "rational.h"
//Определение наибольшего общего делителя
long Rational::gcd(long x,long y)
{ if(y==0)return x; return gcd(y,x%y); }
//-------------------------------------------------------
//Сложение Rational+Rational
Rational Rational::operator+(Rational r)const
{ Rational t;
t.num=num*r.den+den*r.num; t.den=den*r.den;
t.Reduce(); return t; }
//-------------------------------------------------------
//Вычитание Rational-Rational
Rational Rational::operator-(Rational r)const
{ Rational t;
t.num=num*r.den-den*r.num; t.den=den*r.den;
t.Reduce(); return t; }
//--------------------------------------------------------
//Умножение Rational*Rational
Rational Rational::operator*(Rational r)const
{ Rational t;
t.num=num*r.num; t.den=den*r.den; t.Reduce();
return t; }
//-------------------------------------------------------
//Деление Rational/Rational
Rational Rational::operator/(Rational r)const
{ Rational t=Rational(num*r.den,den*r.num);
t.Reduce(); return t; }
//-------------------------------------------------------
//сравнение на ==
int Rational::operator==(Rational r)const
{ return num*r.den==den*r.num; }
//-------------------------------------------------------
// сравнение на >
int Rational::operator>(Rational r)const
{ return num*r.den>den*r.num; }
//-------------------------------------------------------
// сравнение на <
int Rational::operator<(Rational r)const
{ return num*r.den<den*r.num; }
//--------------------------------------------------------
//унарный минус
Rational Rational::operator-(void)const
{ return Rational(-num, den); }
//-------------------------------------------------------
//ввод в формате P/Q (дружественная функция)
istream& operator>>(istream& t,Rational &r)
{ char c; //для чтения разделителя /
t>>r.num>>c>>r.den;
if(r.den==0) { cerr<<"Denominator=0!"; exit(1); }
r.Reduce(); return t; }
//-------------------------------------------------------
// вывод в формате P/Q (дружественная функция)
ostream& operator<<(ostream& t,const Rational &r)
{ t<<r.num<<'/'<<r.den; return t; }
//-------------------------------------------------------
//конструктор Rational(p,q)
Rational:: Rational(long p,long q):num(p),den(q)
{ if(den==0) { cerr<<"Denominator=0!"; exit(1); } }
//-------------------------------------------------------
//конструктор Rational(p,q)
Rational:: Rational(int p,int q):num(p),den(q)
{ if(den==0) { cerr<<"Denominator=0!"; exit(1); } }
//-------------------------------------------------------
//конструктор double->Rational
Rational:: Rational(double x)
{ double val1,val2;
val1=100000000L*x; val2=10000000L*x;
num=long(val1-val2); den=90000000L;
Reduce(); }
//------------------------------------------------------
//преобразование Rational->double
Rational::operator double(void)const
{ return double(num)/den; }
//------------------------------------------------------
//Метод – сокращение дроби
void Rational::Reduce(void)
{ long bigdiv,temp;
temp=(num<0)?-num:num;
if(num==0)den=1;
else
{ bigdiv=gcd(temp,den);
if(bigdiv>1)
{ num /= bigdiv; den /= bigdiv; }
}
}
//---------------------------------------------------------
//Метод – извлечение числителя
long Rational::GetNum(void)const
{ return num; }
//---------------------------------------------------------
//Метод – извлечение знаменателя
long Rational::GetDen(void)const
{ return den; }
//---------------------------------------------------------
Объявление класса начинается со служебного слова class, вслед за которым указывается имя класса. Затем в фигурных скобках следует описание класса. Присутствующие в нем служебные слова private и public предшествуют данным и функциям, объявляемым как личные (приватные) и общедоступные компоненты класса. К личным компонентам класса имеют доступ только функции, описанные в классе (так называемые члены-функции), а также функции и классы, причисленные к друзьям класса (их описания сопровождаются добавкой friend). В описании класса может присутствовать несколько групп, выделенных как личные и общедоступные, порядок их роли не играет. Но если в самом начале описания класса объявлены члены-данные и члены-функции без указания права собственности, то они считаются приватными.
В нашем примере члены-данные объявлены приватными, поэтому в программе, использующей данный класс, нельзя воспользоваться следующим оператором:
Rational A;
A.num=1; A.den=3;
Это означает, что прямой доступ к членам-данным для внешних пользователей запрещен. Объявленный объект можно проинициализировать либо с помощью соответствующего конструктора, либо прибегнуть к специальным функциям или методам типа SetNum и SetDen (правда, в приведенном тексте эти функции отсутствуют, мы ограничились только методами GetNum и GetDen). Такая мера предосторожности позволяет проконтролировать, не нарушил ли программист диапазон данных, предусмотренный для тех или иных полей.
За пределы описания класса вынесены описания его членов-функций. Но для того, чтобы подчеркнуть их принадлежность к классу, перед именем функции расположено специальное указание – Rational::. Обратите внимание на то, что функции-друзья таким свойством не обладают. Однако им разрешен доступ к приватным компонентам класса.
В описании членов-функций класса Rational присутствуют обычные функции и методы. Основное отличие метода от функции заключается в способе обращения:
k = fun(obj1,obj2); //вызов обычной функции с аргументами obj1 и obj2
k = obj1.met(obj2); //вызов метода с аналогичным набором параметров
Говорят, что метод применяется к объекту и "знает" все его характеристики, поэтому количество параметров в методе на один меньше. Вообще говоря, эта информация об объекте поступает в метод в качестве неявного параметра – указателя this (от англ. – этот). В приведенном примере сокращение дроби осуществляется методом Reduce и соответствующее обращение с объектом тип Rational выглядит так: t.Reduce(). А в реализации класса с использованием структуры обращение к этой же процедуре имело вид: Reduce(t).
Среди членов-функций класса Rational довольно много функций, повторяющих ранее приводившиеся тексты программ. Обратим внимание лишь на некоторые особенности новой реализации. Во-первых, сами дроби представляются и на вводе, и на выводе в формате num/den. Во-вторых, в новой версии допускается работа с отрицательными дробями. Наконец, представляет интерес оригинальный алгоритм преобразования данных типа double в формат Rational.
Ниже приводится пример программы, использующей новый тип данных. Обращаем ваше внимание на то, что описание класса не создает никаких объектов. Объекты типа Rational появляются только в результате их объявления с использованием тех или иных конструкторов. Каждому объекту выделяется соответствующий участок оперативной памяти для хранения данных, а вот функции и методы, обслуживающие дробно-рациональные данные, не дублируются. Единственная их копия обслуживает все созданные объекты.
#include <iostream.h>
#include <conio.h>
#include "rational.cpp"
void main()
{ Rational r1(5),r2,r3;
double d;
d=r1.GetNum();
cout<<"d="<<d<<endl;
cout<<"1.Rational - value 5 is "<<r1<<endl;
cout<<"2.Input Rational number: ";
cin>>r1;
d=double(r1);
cout<<"Equivalent of double: "<<d<<endl;
cout<<"3.Input two Rational number: ";
cin>>r1>>r2;
cout<<"Results: "<<endl;
cout<<r1<<" + "<<r2<<"="<<(r1+r2)<<endl;
cout<<r1<<" - "<<r2<<"="<<(r1-r2)<<endl;
cout<<r1<<" * "<<r2<<"="<<(r1*r2)<<endl;
cout<<r1<<" / "<<r2<<"="<<(r1/r2)<<endl;
if(r1<r2)
cout<<"Relation < : " <<r1<<"<"<<r2<<endl;
if(r1==r2)
cout<<"Relation = : " <<r1<<"="<<r2<<endl;
if(r1>r2)
cout<<"Relation > : " <<r1<<" > "<<r2<<endl;
cout<<"4.Input double number: ";
cin>>d;
r1=d;
cout<<"Convert to Rational: "<<r1<<endl;
d=r1;
cout<<"Convert to double: "<<d<<endl;
getch();
}
//=== Результат работы ===