
- •ПРОГРАММИРОВАНИЕ НА ЯЗЫКАХ С И С++
- •СОДЕРЖАНИЕ
- •ВВЕДЕНИЕ
- •1. ЛЕКСИКА ЯЗЫКА C
- •1.1. РАЗДЕЛИТЕЛИ
- •1.2. КОММЕНТАРИИ
- •2. ДАННЫЕ И ОПЕРАЦИИ
- •2.1. БАЗОВЫЕ ТИПЫ ДАННЫХ
- •Наименование
- •Диапазон
- •2.2. КОНСТАНТЫ
- •Целые константы
- •Константы с плавающей точкой
- •Символьные константы
- •2.3. ОПРЕДЕЛЕНИЕ ПЕРЕМЕННЫХ И ТИПИЗИРОВАННЫХ КОНСТАНТ
- •2.4. ОДНОМЕРНЫЙ МАССИВ
- •2.5. СТРОКИ, СТРОКОВЫЕ КОНСТАНТЫ
- •2.6. УКАЗАТЕЛИ
- •2.7. ПЕРЕИМЕНОВАНИЯ ТИПОВ
- •2.8. СТРУКТУРЫ
- •2.9. МАССИВЫ СТРУКТУР
- •2.10. МНОГОМЕРНЫЕ МАССИВЫ
- •2.11. ОБЪЕДИНЕНИЕ
- •2.12. ПЕРЕЧИСЛЕНИЕ
- •2.13. БИТОВОЕ ПОЛЕ ( ЗАПИСЬ )
- •3. ОПЕРАЦИИ И ВЫРАЖЕНИЯ
- •3.1. АРИФМЕТИЧЕСКИЕ ОПЕРАЦИИ
- •3.2. ОПЕРАЦИИ СРАВНЕНИЯ
- •3.3. ЛОГИЧЕСКИЕ ОПЕРАЦИИ
- •3.4. ПОБИТОВЫЕ (ПОРАЗРЯДНЫЕ) ОПЕРАЦИИ
- •3.5. ОПЕРАЦИИ ПРИСВАИВАНИЯ
- •3.6. АДРЕСНЫЕ ОПЕРАЦИИ
- •3.7. ДОСТУП К ПОЛЯМ СТРУКТУР, ОБЪЕДИНЕНИЙ И ЗАПИСЕЙ
- •3.8. ДРУГИЕ ОПЕРАЦИИ
- •3.9. ПОРЯДОК ВЫПОЛНЕНИЯ ОПЕРАЦИЙ В ВЫРАЖЕНИИ
- •4. ОПЕРАТОРЫ
- •4.1. ОПЕРАТОР-ВЫРАЖЕНИЕ
- •4.2. ПУСТОЙ ОПЕРАТОР
- •4.3. СОСТАВНОЙ ОПЕРАТОР
- •4.4. УСЛОВНЫЕ ОПЕРАТОРЫ
- •4.5. ОПЕРАТОРЫ ЦИКЛА
- •while( выражение) оператор;
- •do оператор while ( выражение );
- •for( выражение1; выражение2; выражение3 ) оператор;
- •4.6. ОПЕРАТОРЫ ПЕРЕХОДА
- •break;
- •switch
- •continue;
- •goto метка;
- •5. ФУНКЦИИ. КЛАССЫ ПАМЯТИ
- •5.1. ФУНКЦИИ
- •5.2. КЛАССЫ ПАМЯТИ
- •класс памяти
- •ключевое слово
- •существование
- •видимость
- •5.3. ВОЗВРАЩЕНИЕ ЗНАЧЕНИЙ
- •5.4. АРГУМЕНТЫ ФУНКЦИИ
- •6. ФУНКЦИИ ВВОДА С КЛАВИАТУРЫ И ВЫВОДА НА ДИСПЛЕЙ
- •6.1. ФОРМАТИРОВАННЫЙ ВЫВОД
- •6.2. ФОРМАТИРОВАННЫЙ ВВОД
- •6.3. ВВОД СТРОКИ
- •6.4. ВЫВОД СТРОКИ
- •6.5. ВВОД СИМВОЛА
- •6.6. ВЫВОД СИМВОЛА
- •7. ПРЕПРОЦЕССОР
- •7.1. ВКЛЮЧЕНИЕ ФАЙЛОВ
- •7.2. ВЫПОЛНЕНИЕ ПОДСТАНОВОК
- •7.3. УСЛОВНАЯ КОМПИЛЯЦИЯ
- •8. ДИНАМИЧЕСКОЕ РАСПРЕДЕЛЕНИЕ ПАМЯТИ, СПИСКИ
- •8.1. ВЫДЕЛЕНИЕ И ОСВОБОЖДЕНИЕ ДИНАМИЧЕСКОЙ ПАМЯТИ
- •8.2. СПИСКИ
- •Рис.1. Структура однонаправленного линейного списка
- •9. ФАЙЛЫ
- •9.1. ОТКРЫТИЕ И ЗАКРЫТИЕ ФАЙЛОВ
- •9.2. ЧТЕНИЕ И ЗАПИСЬ В ФАЙЛ
- •9.3. ПРОИЗВОЛЬНЫЙ ДОСТУП К ФАЙЛУ
- •10. ЛАБОРАТОРНЫЕ РАБОТЫ
- •10.1. МАССИВЫ И СИМВОЛЬНЫЕ СТРОКИ
- •Цель работы
- •Пример выполнения работы
- •Варианты заданий
- •10.2. ФУНКЦИИ
- •Цель работы
- •Пример выполнения работы
- •Варианты заданий
- •10.3. СТРУКТУРЫ И МАССИВЫ СТРУКТУР
- •Цель работы
- •Пример выполнения работы
- •Варианты заданий
- •10.4. ДИНАМИЧЕСКОЕ РАСПРЕДЕЛЕНИЕ ПАМЯТИ, СПИСКИ
- •Цель работы
- •Пример выполнения работы
- •Варианты заданий
- •10.5. РАБОТА С ФАЙЛАМИ
- •Цель работы
- •Пример выполнения работы
- •Варианты заданий
- •11. ВВЕДЕНИЕ В C++
- •12. КЛАССЫ
- •12.1. ОБЩИЕ ПОЛОЖЕНИЯ
- •12.2. КОНСТРУКТОРЫ И ДЕСТРУКТОРЫ
- •12.3. СОЗДАНИЕ И ВЫЗОВ ОБЪЕКТОВ
- •12.4. ДРУЖЕСТВЕННЫЕ ФУНКЦИИ
- •12.5. ПЕPЕГPУЖЕННЫЕ ОПЕPАЦИИ И ФУНКЦИИ
- •12.6. ПАРАМЕТРИЗИРОВАННЫЕ КЛАССЫ
- •12.7. ПРЕОБРАЗОВАНИЕ ТИПОВ
- •12.8. СТАТИЧЕСКИЕ КОМПОНЕНТЫ КЛАССА
- •13. ПРОИЗВОДНЫЕ КЛАССЫ, НАСЛЕДОВАНИЕ
- •13.1. ИЕРАРХИЯ КЛАССОВ
- •13.2. ДОСТУП К НАСЛЕДУЕМЫМ КОМПОНЕНТАМ
- •Доступ в базовом классе
- •Модификатор доступа
- •14. ВИРТУАЛЬНЫЕ ФУНКЦИИ
- •14.1. ОБЩИЕ ПОЛОЖЕНИЯ
- •14.2. ВИРТУАЛЬНЫЕ ДЕСТРУКТОРЫ
- •14.3. АБСТРАКТНЫЕ КЛАССЫ
- •15.1. ВЫВОД ДАННЫХ
- •15.2. ВВОД ДАННЫХ
- •15.3. ВВОД-ВЫВОД ДАННЫХ, ОПРЕДЕЛЕННЫХ ПОЛЬЗОВАТЕЛЕМ
- •15.4. РАБОТА С ДИСКОВЫМИ ФАЙЛАМИ
- •15.5. ОБРАБОТКА ИСКЛЮЧЕНИЙ
- •ЛИТЕРАТУРА
– 43 –
Например, для класса Complex можно определить функции getR и getI как дружественные:
friend double getR( Сomplex a){ return a.re;} friend double getI( Сomplex a){ return a.im;}
Обращение к ним следующее: cin>>getR(m2); cin>>getI(m2);
•при необходимости использовать методы одного класса для обработки закрытых дан-
ных другого класса. Объявление функции производится по следующей схеме: class Y {
...
void fy (){...} //Определение функции класса Y
...
};
class X {
...
void fx(){...}
friend void Y::fy();// Объявление функции fy дружественной классу X
...
};
Пример
#include <iostream.h>
class X; // Объявление класса Х class Y {
int a; public:
Y(int n):a(n){}; // Форма записи аналогична {a=n;} void display(X* pX); //Функция-компонент класса Y
}; class X{
int a; public:
X(int n):a(n){};
friend void Y::display(X* pX); .// Функция объявляется дружественной классу Х и
}; // имеет доступ к полю а как класса Х, так и класса
Y
void Y::display(X* pX){ cout<<pX->a<<'\t'<<a<<endl;
}
void main(){ X mx(100); Y my(200);
my.display(&mx); |
|
} |
|
Результат на дисплее: 100 |
200 |
Все функции одного класса могут быть дружественными другому классу.
12.5. ПЕPЕГPУЖЕННЫЕ ОПЕPАЦИИ И ФУНКЦИИ
Если действие какой-либо операции расширено на операнды, тип которых отличается от типа операндов, для которых создавалась операция, или вообще знак операции не соответствует общепринятому смыслу операции, то говорят, что операция перегружена. Например, стандартная операция сложения может быть перегружена для строковых данных, комплексных чисел и т.п. Естественно, что перегруженность сохраняет смысл только для данных того класса, в котором она произведена. Перегрузка операций производится с помощью специальной функции operator согласно следующей форме:
тип operator знак_опеpации(типы аpгументов){... }.
© 1998 Калачев Д.П., Лутай В.Н.
– 44 –
Перегруженная операция может быть определена как компонент класса; в этом случае она имеет один параметр или вообще не имеет параметров. У дружественной перегруженной операции может быть один или два параметра. Поэтому бинарные операции следует перегружать как дружественные.
Пpимеp для класса Сomplex: class Complex {
double re,im; public:
.............
friend Complex operator+(Complex, Complex); };
Complex operator+( Complex l, Complex m) { return Complex (l.re+m.re, l.im + m.im);}
main(){
Complex m1(5,2),m2(6,6),m3(0); m3=m1+m2;
return 0;
}
Операция ’+’ вне класса Complex действует как обычно.
(Функция operator+(a,b) позволяет использовать краткую форму записи a+b. Мы могли бы без перегрузки записать m3=operator+(m1,m2); это относится и к другим операциям)
Перегружаться могут практически все операции (исключение – оператор принадлежности ::, операторы . .* ?: sizeof и символ #). При этом функция operator не изменяет приоритеты операций и число операндов, определенных в языке.
Функция operator для переопределения бинарных операций может быть описана как функция-компонент класса с одним аргументом (например, для операции сложения a.operator+(b) ) или как дружественная функция с двумя аргументами operator+(a,b) ); унарная операция – как функция-компонент без аргументов или как дружественная функция с одним аргументом. Четыре функции operator=, operator[], operator() и operator-> должны быть нестатическими компонентами-функциями класса.
Все перегруженные операции, кроме ’=’ наследуются. Относительно последней нужно отметить, что по умолчанию при ее использовании выполняется почленное копирование одного объекта в другой; например, если для класса String запишем:
String s1(10), s2(10); s1=s2;
то в результате операции присваивания s1 будет иметь тот же указатель, что и s2; при изменении s2 изменяется и s1. Поэтому операцию присваивания целесообразно перегружать с выделением памяти для первого массива по типу:
X&x:: operator=(X&)
Для класса String можно записать: void String::operator=(String& a) {
if (this==&a) return; //для предотвращения s1=s1; delete charPtr; //чистка памяти
length=a.length;
charPtr= new char[length+1]; strcpy(charPtr,a.charPtr);
}
Перегруженные функции имеют тот же смысл, что и операции: функция с одним и тем же именем работает по-pазному с различными типами аргументов. Примером является набор конструкторов в классе String.
Функции new и delete перегружаются c помощью функции operator следующим образом:
void* operator new(size_t t [,список аргументов]),
где size_t – первый и единственный обязательный аргумент.
© 1998 Калачев Д.П., Лутай В.Н.
– 45 –
Например, перегрузка
void* operator new(size_t t, int n){ return new char[t*n]; }
позволяет добавить к стандартной функции new еще один аргумент – количество блоков памяти размером size_t каждый. Предположим, что функция вызывается с n=5 и t=double: double *d=new(5) double;
Значение t в теле функции станет равным sizeof(double)=8 (для персональных компьютеров); в результате под переменную будет выделено 5*8=40 байтов памяти.
12.6. ПАРАМЕТРИЗИРОВАННЫЕ КЛАССЫ
Параметризированный класс представляет собой некоторый шаблон (template), определяющий правила построения отдельного класса из множества разрешенных. Рассмотрим пример шаблона для построения стека (Stack) элементов типа Type:
template <class Type> class Stack{
Type *top, *sp; // указатели на начало и текущее состояние стека int size; // размер стека
public:
Stack(int n) {top=sp=new Type[size=n]; } ~Stack() {delete[] top; }
//Операция += перегружается как операция ”заталкивания” в стек;
//cerr - стандартный поток для вывода сообщений об ошибках на дисплей void operator+=(Type v) {
if (size>sp-top) *sp++=v; else cerr<<”Error\n”; }
//Операция --перегружается как операция извлечения из стека
Type operator--() {
if (sp==top){ cerr<<”Error:\t”; return *sp; } return *--sp; }
};
Имея шаблон, можно объявить стек с любым типом элементов: void main () {
Stack <int>si(2); // Объявление стека целых размером 2 si+=100; // Целое заталкивается в стек
si +=234;
si+=567; // Ошибка-результат Error cout<<--si<<endl; // Извлекается 234 cout<<--si<<endl; // Извлекается 100 cout<<--si<<endl; // Ошибка - результат Error:100
}
Здесь методами класса являются встроенные функции. Если бы их определяли вне класса, то следовало бы их записать как параметризированные функции:
template <class Type> Type Stack<Type>:: operator--() { if (sp==top){ cerr<<“Error:\t”; return *sp; }
return *--sp; }
template <class Type> void Stack<Type>:: operator+=(Type v) { if (size>sp-top) *sp++=v; else cerr<<“Error\n”; } )
Параметром шаблона, который передается в конструируемый класс, является Type. Его значением может быть не только встроенный, но и введенный пользователем тип. Для того, чтобы построить, например, стек комплексных чисел, надо описать в программе соответствующий класс, а затем объявить Stack<Complex>ss(3);
К параметризированным классам примыкают контейнеры классов. Контейнер – множество классов, которые отличаются только типами используемых в них данных; контейнеры содержат типовые структуры и типовые операции над данными, входящими в эти структуры. В библиотеке системы Borland C++ содержатся контейнеры, которые включают бинарные деревья, кэш-таблицы, списки, векторы, массивы, очереди, словари, множества и стеки.
© 1998 Калачев Д.П., Лутай В.Н.
– 46 –
Из этих структур можно конструировать классы с различными типами данных. Ниже приведена программа построения массива объектов типа double c использованием класса TSArrayAsVector, для которого задан шаблон в библиотеке системы.
#include <iostream.h> #include <classlib\arrays.h> void main(void){
TSArrayAsVector<double>a(10); // a - контейнер объектов типа double, //10 - число объектов в контейнере;объекты в контейнере отсортированы
double j;
for(int i=0; i<10; i++){ cin>>j;
a.Add(j); } // добавление объектов в контейнер for (i=0; i<10; i++)
cout<<a[i]<<'\t';
}
В общем случае конструктор TSArrayAsVector имеет следующий вид:
TSArrayAsVector(int upper, int lower=0,int delta=0),
где upper – верхняя граница индекса, lower - нижняя его граница , delta – приращение числа объектов. Если в предыдущем примере записать ненулевое приращение, например, объявить a(10,0,10), то после введения 10-го объекта в массив автоматически добавится еще 10 полей для новых объектов. Это позволяет решать проблему расширения массивов и не требует оценки размера массива на этапе проектирования программы.
12.7. ПРЕОБРАЗОВАНИЕ ТИПОВ
Конструктор класса, вызванный при создании объектов, выполняет преобразование переменных: базовый_тип->класс. Для обратного преобразования из заданного класса в базовый тип следует определить преобразование типа, добавив в описание класса функцию operator:
class Myclass { double d;
. . .
public:
Myclass(double x) : d(x){ } operator double() {return d; }
};
void main() { Myclass mc1(10.83); double mc2; mc2=mc1;
}
Аргументом оператора return может быть выражение (*this) .
12.8. СТАТИЧЕСКИЕ КОМПОНЕНТЫ КЛАССА
Компоненты класса как поля, так и функции можно объявлять статическими (static). Эти элементы занимают статическую область памяти и являются общими для всех объектов класса.
К закрытым статическим компонентам возможен доступ через интерфейс класса или со спецификатором класса. Статические компоненты в отличие от нестатических полей могут быть инициализированы, даже если не создан ни один объект класса. Статический компонент не имеет указателя this. Статическая функция видима только в пределах класса. При-
ведем пример:
void f() {cout<<“Я - глобальная функция”<<endl;} //Глобальная функция class MyClass {
static int a; public:
© 1998 Калачев Д.П., Лутай В.Н.