- •1.1.1 Пример программы, выводящей текст на экран (пример 1)
- •1.1.2 Директивы препроцессору (подключение заголовочных файлов)
- •1.1.3 Комментарии
- •1.1.4 Функции
- •1.1.5 Ввод и вывод на экран
- •1.2. Переменные и их объявление
- •1.2.1 Пример программы cложения целых чисел (пример 2)
- •1.2.2 Переменные и их объявление
- •1.3. Арифметические операторы
- •1.3.1 Примеры арифметических операций (пример 3)
- •1.3.2 Группировка подвыражений с помощью скобок
- •1.4. Логические выражения и оператор if
- •1.4.1 Условные конструкции. Пример условных конструкций (пример 4)
- •1.4.2 Логические выражения. Логические операции и, или, не (пример 5)
- •1.4.3 Типичные ошибки
- •1.4.4 Вложенные условия
- •1.5. Арифметический логический оператор (пример 6)
- •1.6. Селективные конструкции
- •1.6.1 Селективные конструкции. Пример определения оценки в зависимости от количества баллов (пример 6)
- •1.6.2 Оператор Switch. Пример меню с выбором действия (пример 7)
- •1.7. Циклы while и do…while
- •1.7.1 Цикл с предусловием while. Пример возведения в степень в цикле (пример 8)
- •1.7.2 Цикл с постусловием do...While
- •1.8. Пошаговый цикл for
- •1.8.1 Пример работы оператора for - вычисление суммы чисел (пример 9)
- •1.8.2 Пошаговый цикл for
- •1.8.3 Операторы break и continue
- •1.8.4 Пример вычисление факториала (пример 10)
- •1.9. Функции
- •1.9.1 Использование функций библиотеки stl (пример 11)
- •1.9.2 Определение новых функций
- •1.9.3 Пример функции (пример 12)
- •1.10. Размещение программ и данных в памяти
- •1.11. Ссылки и указатели
- •1.11.1. Ссылки
- •1.11.2. Указатели
- •1.11.3. Передача параметров в функцию по ссылке и указателю
- •2.2 Организация ввода/вывода
- •2.3 Строковые переменные и константы
- •2.4 Математические функции
- •3.1. Массивы
- •3.1.1. Одномерный массив
- •3.1.2. Динамическое размещение одномерного массива
- •3.1.3. Передача массива в функцию (пример 3.1)
- •3.1.4. Двумерный массив
- •3.1.5. Динамическое размещение двумерного массива (пример 3.2)
- •3.2 Контейнеры
- •3.3. Вектор vector (пример 3.3)
- •4.4. Список list
- •3.4.1. Списки
- •3.4.2. Итераторы
- •3.4.3. Пример работы со списком с использованием итераторов (пример 3.4)
- •3.5. Очереди и стек
- •3.5.1. Двусторонняя очередь deque (пример 3.5)
- •3.5.2. Стек stack
- •3.5.3. Очередь queue
- •3.6. Ассоциативные контейнеры
- •3.6.1. Контейнер map (пример 3.7)
- •3.6.2. Контейнер set (пример 3.8)
- •3.7. Алгоритмы
- •4.1 Структуры
- •4.1.1. Пример 4.1. Структура для работы с компонентами цвета
- •4.1.2. Передача абстрактных типов в функцию
- •4.1.3. Создание функций-членов для абстрактного типа данных. Пример 4.2. Структура для работы с компонентами цвета со встроенной функцией.
- •4.2. Классы
- •4.2.1. Пример 4.3. Класс Линза
- •4.2.2. Директивы препроцессору # if ! defined, # endif (проверка на повторное подключение)
- •4.2.3. Тип доступа к членам класса
- •4.2.4. Принципы объектно-ориентированного проектирования
- •4.2.5. Типы функций-членов класса
- •4.3 Конструкторы и деструкторы класса
- •4.3.1. Конструкторы
- •4.3.2. Деструктор (пример 4.4. Конструктор и деструктор класса Матрица)
- •4.3.3. Проверка правильности параметров. Исключительные ситуации
- •4.4. Модификаторы, селекторы и другие члены классов
- •4.4.1. Модификаторы и селекторы
- •4.4.2. Ключевые слова const и inline
- •4.4.3. Функции-утилиты
- •4.4.4. Сохраняемость
- •5.1. Типы наследования. Видимость членов классов
- •5.1.1. Наследование
- •5.1.2. Пример 5.1. Линза и зеркало как оптические детали
- •5.1.3. Последовательность вызова конструкторов
- •5.1.4. Типы наследования. Видимость членов классов
- •5.1.5. Множественное наследование
- •5.2. Виртуальные функции. Абстрактные классы
- •5.2.1. Виртуальные функции
- •5.2.2. Абстрактные классы
- •6. Полиморфизм
- •6.1. Перегрузка функций
- •6.1.1. Перегрузка функций
- •6.1.2. Преобразование типов
- •6.1.3. Параметры функций по умолчанию
- •6.2. Перегрузка операторов
- •6.2.1. Пример 6.1 (класс Complex (комплексное число))
- •6.2.6. Перегрузка операторов с присваиванием
- •6.2.7. Перегрузка преобразования типов
- •6.2.8. Перегрузка оператора доступа по индексу
- •6.2.9. Перегрузка операторов ввода/вывода
- •6.2.10. Неперегружаемые операторы
- •6.3. Шаблоны функций и классов
- •6.3.1. Шаблоны функций. Пример 6.2 (шаблон функции)
- •6.3.2. Шаблоны функций с несколькими параметрами. Пример 6.3 (шаблон функции с несколькими параметрами)
- •6.3.3. Шаблоны классов. Пример 6.4 (шаблон класса Комплексное число)
- •6.4. Объекты-функции. Предикаты
- •6.4.1. Объекты-функции. Пример 6.5 (использование объектов-функций)
- •6.4.2. Предикаты. Пример 6.6 (использование предикатов)
6.1.3. Параметры функций по умолчанию
Еще один случай проявления принципа полиморфизма - использование аргументов функций по умолчанию. При использовании параметров по умолчанию, при описании функции указываются значение, которые будут присвоены параметрам, если они будут отсутствовать при вызове. В реализации функции ничего не меняется, например:
// описание полного конструктора с параметрами по умолчанию
Lens(double r1, double r2, double D=0., double d=0., double n=1.);
// реализация полного конструктора с параметрами по умолчанию
Lens::Lens(double r1, double r2, double D, double d, double n)
: m_r1(r1)
, m_r2(r2)
, m_d(d)
, m_D(D)
, m_n(n)
{
}
При вызове такого конструктора можно указывать набор параметров от 2 до 5, при этом отсутствующие параметры будут инициализированы параметрами по умолчанию. Обратите внимание, что отсутствующие параметры всегда должны быть в конце перечисления.
Lens lens1(100., -100.); // в результате: r1=100., r2=-100., D=0., d=0., n=1.
Lens lens1(100., -100., 50.); // в результате: r1=100., r2=-100., D=50., d=0., n=1.
Lens lens1(100., -100., 50., 10.); // в результате: r1=100., r2=-100., D=50., d=10., n=1.
Lens lens1(100., -100., 50., 10., 1.5); // в результате: r1=100., r2=-100., D=0., d=10., n=1.5
Lens lens1(100., -100., 1.5); // в результате: r1=100., r2=-100., D=1.5, d=0., n=1.
6.2. Перегрузка операторов
Перегрузка операторов позволяет использовать для абстрактных типов данных привычные операторы, например + для сложения или объединения двух объектов, = для присвоения одного экземпляра объекта другому и т.д. Комплексная арифметика, матричная алгебра, символьные строки - это лишь немногие примеры, где удобно использовать перегруженные операторы.
Например, для комплексных чисел (типа данных Complex) можно перегрузить следующие операторы:
* – комплексное умножение
+ – комплексное сложение
~ – комплексное сопряжение
Complex x,y,z;
z=x*y;
z=x.operator*(y); // явный вызов оператора
class Complex
{
…
Complex operator*(const Complex& other) const;
…
}
Исходя из этой записи, нетрудно догадаться, как осуществляется перегрузка операторов для абстрактных типов данных. Просто нужно определить функцию-член с именем operator* или любой другой оператор.
Рассмотрим пример класса Complex, а затем разберем подробно перегрузку некоторых типовых операторов.
6.2.1. Пример 6.1 (класс Complex (комплексное число))
/////////////////////////////////////////////////////////////////////////////
// Прикладное программирование
// Пример 6.1. Класс Комплексное число
// complex.h
//
// Кафедра Прикладной и компьютерной оптики, http://aco.ifmo.ru
// СПб НИУ ИТМО
/////////////////////////////////////////////////////////////////////////////
// проверка на повторное подключение файла
#if !defined COMPLEX_H
#define COMPLEX_H
#include <iostream>
using namespace std;
/////////////////////////////////////////////////////////////////////////////
// класс Комплексное число
class Complex
{
private:
// вещественная и мнимая часть комплексного числа
double m_re, m_im;
public:
// конструкторы
Complex();
Complex(double re, double im=0);
Complex(const Complex& other);
// получение параметров комплексного числа
double GetRe() const;
double GetIm() const;
// изменение параметров комплексного числа
void Set(double re, double im=0.);
// оператор умножения
Complex operator*(const Complex& other) const;
// оператор умножения на число
Complex operator*(const double& other) const;
// оператор умножения с присваиванием
Complex& operator*=(const Complex& other);
// оператор присваивания
Complex& operator=(const Complex& other);
// оператор равенства
bool operator== (const Complex& other) const;
// оператор сопряжения комплексного числа
Complex operator~() const;
// унарный минус
Complex operator-() const;
// ввод/вывод комплексного числа
friend ostream& operator<< (ostream& out, const Complex& x);
friend istream& operator>> (istream& out, Complex& x);
// преобразование типа Complex в double
operator double() const;
};
/////////////////////////////////////////////////////////////////////////////
// получение вещественной части комплексного числа
inline double Complex::GetRe() const
{
return m_re;
}
/////////////////////////////////////////////////////////////////////////////
// получение мнимой части комплексного числа
inline double Complex::GetIm() const
{
return m_im;
}
/////////////////////////////////////////////////////////////////////////////
// изменение параметров комплексного числа
inline void Complex::Set(double re, double im)
{
m_re=re;
m_im=im;
}
/////////////////////////////////////////////////////////////////////////////
#endif //defined COMPLEX_H
/////////////////////////////////////////////////////////////////////////////
// Прикладное программирование
// Пример 6.1. Класс Комплексное число
// complex.cpp
//
// Кафедра Прикладной и компьютерной оптики, http://aco.ifmo.ru
// СПб НИУ ИТМО
/////////////////////////////////////////////////////////////////////////////
// подключение описания класса
#include "complex.h"
/////////////////////////////////////////////////////////////////////////////
// конструктор по умолчанию
Complex::Complex()
: m_re(0.)
, m_im(0.)
{
}
/////////////////////////////////////////////////////////////////////////////
// полный конструктор
Complex::Complex(double re, double im)
: m_re(re)
, m_im(im)
{
}
/////////////////////////////////////////////////////////////////////////////
// конструктор копирования
Complex::Complex(const Complex& x)
: m_re(x.m_re)
, m_im(x.m_im)
{
}
/////////////////////////////////////////////////////////////////////////////
// оператор умножения
Complex Complex::operator*(const Complex& other) const
{
return Complex(m_re*other.m_re-m_im*other.m_im,
m_re*other.m_im-m_im*other.m_re);
}
/////////////////////////////////////////////////////////////////////////////
// оператор умножения на число
Complex Complex::operator*(const double& other) const
{
return Complex(m_re*other, m_im*other);
}
/////////////////////////////////////////////////////////////////////////////
// оператор умножения с присваиванием
Complex& Complex::operator*=(const Complex& other)
{
Complex temp(*this);
m_re=temp.m_re*other.m_re - temp.m_im*other.m_im;
m_im=temp.m_re*other.m_im + temp.m_im*other.m_re;
return (*this);
}
/////////////////////////////////////////////////////////////////////////////
// унарный минус
Complex Complex::operator-() const
{
return Complex(-m_re, -m_im);
}
/////////////////////////////////////////////////////////////////////////////
// оператор сопряжения комплексного числа
Complex Complex::operator~() const
{
return Complex(m_re, -m_im);
}
/////////////////////////////////////////////////////////////////////////////
// оператор равенства
bool Complex::operator== (const Complex& other) const
{
return (m_re == other.m_re && m_im == other.m_im);
}
/////////////////////////////////////////////////////////////////////////////
// оператор присваивания
Complex& Complex::operator=(const Complex& other)
{
if(this != &other)
{
m_re=other.m_re;
m_im=other.m_im;
}
return *this;
}
/////////////////////////////////////////////////////////////////////////////
// преобразование типа Complex в double
Complex::operator double() const
{
return (m_re*m_re-m_im*m_im);
}
/////////////////////////////////////////////////////////////////////////////
// вывод комплексного числа на экран
ostream& operator<< (ostream& out, const Complex& x)
{
return (out<<"("<<x.m_re<<","<<x.m_im<<")");
}
/////////////////////////////////////////////////////////////////////////////
// ввод комплексного числа с клавиатуры
istream& operator>> (istream& in, Complex& x)
{
return (in>>x.m_re>>x.m_im);
}
/////////////////////////////////////////////////////////////////////////////
/////////////////////////////////////////////////////////////////////////////
// Прикладное программирование
// Пример 6.1. Класс Комплексное число
// test_complex.cpp
//
// Кафедра Прикладной и компьютерной оптики, http://aco.ifmo.ru
// СПб НИУ ИТМО
/////////////////////////////////////////////////////////////////////////////
#include <iostream>
using namespace std;
// подключение описания класса
#include "complex.h"
/////////////////////////////////////////////////////////////////////////////
// пример использования класса Complex
void main()
{
Complex x(1,1), y(2,2), res1, res2, res3;
Complex x1;
// тестирование преобразования типов
// преобразование вещественного числа в комплексное при помощи конструктора
res1=Complex(3.14);
cout<<"Complex(3.14): "<<res1<<endl;
// преобразование комплекcного числа в вещественное при помощи перегруженного оператора double
double c=double(res1);
cout<<"double(res1): "<<c<<endl<<endl;
// тестирование арифметических операторов
res1=x*y; // перемножение двух комплексных чисел
res1=x.operator*(y); // то же самое, явный вызов оператора
res2=-x; // унарный минус
res3=~x; // комлексное сопряжение
x*=y; // умножение с присваиванием
cout<<" x*y="<<res1<<endl;
cout<<" -x="<<res2<<endl;
cout<<" ~x="<<res3<<endl;
cout<<"x*=y: "<<x<<endl<<endl;
// тестирование оператора равенства
if(x==y)
cout<<x<<" = "<<y<<endl<<endl;
else
cout<<x<<" != "<<y<<endl<<endl;
// тестирование операторов присваивания
res3=res2=res1=1;
cout<<res1<<"="<<res2<<"="<<res3<<endl;
}
/////////////////////////////////////////////////////////////////////////////
6.2.2. Перегрузка бинарных операторов
// оператор умножения
Complex Complex::operator*(const Complex& other) const
{
return Complex(m_re*other.m_re-m_im*other.m_im,
m_re*other.m_im-m_im*other.m_re);
}
// оператор умножения на число
Complex Complex::operator*(const double& other) const
{
return Complex(m_re*other, m_im*other);
}
// пример использования
Complex x, z;
double y;
z=x*y;
6.2.3. Перегрузка унарных операторов
// унарный минус
Complex Complex::operator-() const
{
return Complex(-m_re, -m_im);
}
// оператор сопряжения комплексного числа
Complex Complex::operator~() const
{
return Complex(m_re, -m_im);
}
// пример использования
Complex x, y;
y=-x;
y=~x;
6.2.4. Перегрузка логических операторов
// оператор равенства
bool Complex::operator== (const Complex& other) const
{
return (m_re == other.m_re && m_im == other.m_im);
}
// пример использования
Complex x, y;
if(x==y)
{
…
}
6.2.5. Перегрузка оператора присваивания
Перегрузку оператора присваивания следует выделить особо. Мы уже рассматривали функцию, которая позволяет инициализировать один экземпляр класса значениями переменных членов хранящихся в другом экземпляре. Это конструктор копирования. Оператор присваивания отличается тем, что новый класс при его выполнении не создаётся, а значения членов одного экземпляра присваиваются членам другого экземпляра. При перегрузке оператора присваивания необходимо руководствоваться следующими правилами:
Аргументом перегруженного оператора присваивания должна быть неизменяемая ссылка на экземпляр данного класса (чтобы случайно не испортить экземпляр).
Перед осуществлением присваивания необходимо осуществить проверку на присваивание самому себе (чтобы не выполнять лишних действий).
Оператор должен осуществлять поэлементное присваивание (то есть должно быть выполнено последовательное присваивание каждой переменной).
Оператор должен возвращать ссылку на самого себя (чтобы оно было похоже на присваивание встроенных типов данных, тогда будет возможна запись x=y=z=1).
// оператор присваивания
Complex& Complex::operator=(const Complex& other)
{
if(this != &other)
{
m_re=other.m_re;
m_im=other.m_im;
}
return *this;
}
// пример использования
Complex x, y, z;
x=y=z=1;
