- •Содержание
- •ВВЕДЕНИЕ
- •1.ОБЪЕКТНО-ОРИЕНТИРОВАННЫЙ ПОДХОД
- •2. ОБЪЕКТНО-ОРИЕНТИРОВАННОЕ ПРОГРАММИРОВАНИЕ
- •2.1. Абстрактные типы данных
- •2.2. Базовые принципы объектно-ориентированного программирования
- •2.3. Основные достоинства языка С++
- •2.4. Особенности языка С++
- •2.4.1. Ключевые слова
- •2.4.2. Константы и переменные
- •2.4.3. Операции
- •2.4.4. Типы данных
- •2.4.5. Передача аргументов функции по умолчанию
- •2.5.1. Объект cin
- •2.5.2. Объект cout
- •2.5.3. Манипуляторы
- •3.1. Объекты
- •3.2. Понятие класса
- •3.3. Конструктор копирования
- •3.4. Конструктор explicit
- •3.5. Указатель this
- •3.6. Встроенные функции (спецификатор inline)
- •3.7. Организация внешнего доступа к локальным компонентам класса (спецификатор friend)
- •3.8. Вложенные классы
- •3.9. Static-члены (данные) класса
- •3.10. Компоненты-функции static и const
- •3.11. Proxi-классы
- •3.12. Ссылки
- •3.12.1. Параметры ссылки
- •3.12.2. Независимые ссылки
- •3.13. Пространства имен
- •3.13.3. Ключевое слово using как объявление
- •3.13.4. Псевдоним пространства имен
- •3.14. Практические приемы ограничения числа объектов класса
- •4. НАСЛЕДОВАНИЕ
- •4.1.1. Конструкторы и деструкторы при наследовании
- •4.2. Виртуальные функции
- •4.3. Абстрактные классы
- •4.4. Виртуальные деструкторы
- •4.6. Виртуальное наследование
- •5.2. Перегрузка операторов
- •5.2.2. Перегрузка унарного оператора
- •5.2.3. Дружественная функция operator
- •5.2.4. Особенности перегрузки операции =
- •5.2.5. Перегрузка оператора []
- •5.2.6. Перегрузка оператора ()
- •5.2.7. Перегрузка оператора ->
- •5.2.8. Перегрузка операторов new и delete
- •5.3. Преобразование типа
- •5.3.1. Явные преобразования типов
- •6. ШАБЛОНЫ
- •6.1. Параметризированные классы
- •6.2. Передача в шаблон класса дополнительных параметров
- •6.3. Шаблоны функций
- •6.4. Совместное использование шаблонов и наследования
- •6.5. Шаблоны класса и friend-функции
- •6.6. Некоторые примеры использования шаблона класса
- •6.6.1. Реализация smart-указателя
- •6.6.2. Задание значений параметров класса по умолчанию
- •7.2. Состояние потока
- •7.3. Строковые потоки
- •7.4. Организация работы с файлами
- •7.5. Организация файла последовательного доступа
- •7.6. Создание файла произвольного доступа
- •7.7. Основные функции классов ios, istream, ostream
- •8. ИСКЛЮЧЕНИЯ В С++
- •8.2. Перенаправление исключительных ситуаций
- •8.3. Исключительная ситуация, генерируемая оператором new
- •8.6. Спецификации исключительных ситуаций
- •8.7. Задание собственного неожиданного обработчика
- •9. СТАНДАРТНАЯ БИБЛИОТЕКА ШАБЛОНОВ (STL)
- •9.3. Категории итераторов
- •9.4. Операции с итераторами
- •9.5. Контейнеры последовательностей
- •9.5.2. Контейнер последовательностей list
- •9.5.3. Контейнер последовательностей deque
- •9.6. Ассоциативные контейнеры
- •9.6.1. Ассоциативный контейнер multiset
- •9.6.2. Ассоциативный контейнер set
- •9.6.3. Ассоциативный контейнер multimap
- •9.7.1. Адаптер stack
- •9.7.2. Адаптер queue
- •9.7.3. Адаптер priority_queue
- •9.8. Алгоритмы
- •9.8.1. Алгоритмы сортировки sort, partial_sort, sort_heap
- •9.8.2. Алгоритмы поиска find, find_if, find_end, binary_search
- •9.8.3. Алгоритмы fill, fill_n, generate и generate_n
- •9.8.4. Алгоритмы equal, mismatch и lexicographical_compare
- •9.8.6. Алгоритмы работы с множествами
- •9.8.7. Алгоритмы swap, iter_swap и swap_ranges
- •9.8.8. Алгоритмы copy, copy_backward, merge, unique и reverse
- •10. ПРИМЕРЫ РЕАЛИЗАЦИИ КОНТЕЙНЕРНЫХ КЛАССОВ
- •10.1. Связанные списки
- •10.1.1. Реализация односвязного списка
- •10.2. Реализация бинарного дерева
- •11. ПРОГРАММИРОВАНИЕ ДЛЯ WINDOWS
- •11.1. Система, управляемая сообщениями
- •11.2. Управление графическим выводом
- •11.3. Контекст устройства
- •11.3.1. Экран
- •11.3.2. Принтер
- •11.3.3. Объект в памяти
- •11.3.4. Информационный контекст
- •11.4. Архитектура, управляемая событиями
- •11.5. Исходный текст программы
- •11.7. Некоторые новые типы данных
- •11.8. Венгерская нотация
- •11.9. Точка входа программы
- •11.11. Создание окна
- •11.12. Цикл обработки сообщений
- •11.13. Оконная процедура
- •11.14. Обработка сообщений
- •11.15. Обработка сообщений функцией DefWindowProc
- •11.16. Синхронные и асинхронные сообщения
- •11.17. Еще один метод получения описателя контекста устройства
- •11.19. Полосы прокрутки
- •Литература
void tree::see(node *u) // функция рекурсивного вывода бинарного дерева
{if(u)
{cout<<"узел содержит: "<<u->inf<<" число встреч "<<u->n<<endl;
if (u->l) see(u->l); |
// вывод левой ветви дерева |
|
if (u->r) see(u->r); |
// вывод правой ветви дерева |
|
} |
|
|
} |
|
|
int main() |
|
|
{ tree t; |
|
Р |
int i; |
|
|
|
|
|
while(1) |
|
|
{ cout<<"вид операции: 1 − создать дерево"<<endl; |
|
|
|
cout<<" |
|
2 − рекурсивный вывод содержимого дерева"<<endl; |
|||||
|
|
cout<<" |
|
3 − нерекурсивный вывод содержимого дерева"<<endl; |
|||||
|
|
cout<<" |
|
4 − добавление элементов в деревоИ"<<endl; |
|||||
|
|
cout<<" |
|
5 − удаление любого элемента из дерева"<<endl; |
|||||
|
|
cout<<" |
|
6 − выход"<<endl; |
|
У |
|||
|
|
|
Г |
||||||
|
|
cin>>i; |
|
|
|
|
|
||
|
|
switch(i) |
|
|
|
|
|
Б |
|
|
|
{ case 1: t.sozd(); break; |
|
|
|
||||
|
|
case 2: t.see(t.root()); |
break; |
|
|
||||
|
|
case 6: return; |
|
|
|
а |
|
||
|
|
} |
|
|
|
к |
|
|
|
|
|
return 0; |
|
|
|
|
|
||
|
} |
|
|
е |
|
|
|
||
} |
|
|
|
|
|
|
|||
|
|
|
|
|
|
|
|
|
|
При небольш й м дификации в функции main() можно создать несколько |
|||||||||
объектов класса tree |
т |
|
|
|
|
||||
, например, используя указатель на объект класса tree, вы- |
|||||||||
|
|
о |
|
|
|
|
|
||
зывать методы к асса для работы с каждым из созданных объектов (бинарных |
|||||||||
деревьев). Это преобразование предлагается выполнить самостоятельно. |
|||||||||
|
|
и |
|
|
|
|
|
|
|
3.3.лКонструктор копирования |
|
|
|||||||
Нео ходимость использования конструктора копирования вызвана тем, |
Бчтоиобъекты наряду со статическими могут содержать и динамические данные. В то же время, например, при передаче объекта в качестве параметра функции в ней создается локальная (в пределах функции) копия этого объекта. При этом указатели обоих объектов будут содержать один и тот же адрес области памяти. При выводе локального объекта из поля видимости функции для его разруше- ния вызывается деструктор. В функцию деструктора входит также освобожде- ние динамической памяти, адрес которой содержит указатель. При окончании работы программы (при вызове деструкторов) производится повторная попытка освободить уже освобожденную ранее память. Это приводит к ошибке. Для устранения этого в класс необходимо добавить конструктор копирования, кото-
31
рый в качестве единственного параметра получает ссылку на объект класса. Общий вид конструктора копирования имеет следующий вид:
имя_класса (const имя_класса & );
В этом конструкторе выполняется выделение памяти и копирование в нее ин- формации из объекта, получаемого по ссылке. Таким образом, указатели для обоих объектов содержат разные адреса памяти, и при вызове деструктора вы- полняется освобождение памяти, соответствующей каждому из объектов.
#include <iostream> |
|
|
||
using namespace std; |
|
Р |
||
#include <stdlib.h> |
|
|||
class cls |
|
|
|
|
{ char *str; |
|
|
|
И |
int dl; |
|
|
|
|
// . . . |
другие данные класса |
|
У |
|
public: |
|
|
Г |
|
|
|
Б |
|
|
cls (); |
// |
конструктор по умолчанию |
|
|
|
|
|||
cls(cls &); |
// |
копирующий конструктор |
|
|
~cls(); |
// |
деструктор |
|
|
|
// . . . другие методы класса |
||||
}; |
|
|
|
|
ка |
cls::cls () |
|
|
|
||
{ dl=10; |
|
|
|
е |
|
|
str=new char[dl]; |
|
|||
} |
|
|
|
|
т |
cls::cls(cls & obj1) |
о |
||||
|
// к пирующий конструктор из obj1 в obj |
||||
{ dl=obj1.dl; |
|
и |
// к пирование длины строки |
||
|
|
|
|||
|
str=new char[dl]; |
|
// выделение памяти “под” строку длиной dl |
||
|
|
л |
|
// копирование строки |
|
} |
strcpy(str,obj1.str); |
|
|||
|
|
|
|
|
|
cls::~cls() |
|
|
|
|
|
|
и |
|
|
|
|
{ delete [] str; |
|
|
|
||
} |
cout<<"бдеструктор"<<endl; |
||||
|
|
|
|
|
|
void fun(cls obj1) |
|
|
|
||
Б{ // код функции |
|
|
|||
|
cout<<" выполняется функция "<<endl; |
}
void main(void)
{cls obj;
//. . .
32
fun(obj); // . . .
}
Если для класса конструктор копирования явно не описан, то компилятор сгенерирует его. При этом значения компоненты-данного одного объекта будут скопированы в компоненту-данное другого объекта. Это допустимо для объек- тов простых классов и недопустимо для объектов, имеющих динамические компоненты-данные (конструируются с использованием операторов динамиче-
3.4.Конструктор explicit ГУИР
ВС++ компилятор для конструктора с одним аргументом может автома- тически выполнять неявные преобразованияБ. В результате этого тип, получае- мый конструктором, преобразуется в объект класса, для которого определен данный конструктор. акского выделения памяти). Таким образом, даже если в классе не используются
|
{ int size; |
|
// |
|
|
е |
|
|
|
|
размерность массива |
||||
|
|
int *ms; |
|
// |
|
указа ль на массив |
|
|
public: |
о |
|
||||
|
|
array(int = 1); |
т |
||||
|
|
и |
|
||||
|
|
~array(); |
|
|
|||
|
|
friend void print(const array&); |
|||||
|
}; |
л |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
array::array(int kl) : size(kl) |
||||||
|
{ cout<<"работает конструктор"<<endl; |
||||||
и |
|
|
|
|
// выделение памяти для массива |
||
|
ms=new int[size]; |
|
|||||
Б |
бfor(int i=0; i<size; i++) ms[i]=0; // инициализация |
||||||
} |
|
|
|
|
|
|
|
array::~array() |
|
|
|
|
|||
{ cout<<"работает деструктор"<<endl; |
delete [] ms;
}
void print(const array& obj)
{ cout<<"выводится массив размерностью"<<obj.size<<endl; for(int i=0; i<obj.size; i++)
33
cout<<obj.ms[i];
cout<<endl;
} |
|
|
|
|
|
|
|
|
void main() |
|
|
|
|
|
|
|
|
{ array obj(10); |
|
|
|
|
|
|
|
|
print(obj); |
// вывод содержимого объекта obj |
|
|
|
|
|||
print(5); |
// преобразование 5 в array и вывод |
|
|
|
|
|||
} |
|
|
|
|
|
|
|
Р |
В результате выполнения программы получим: |
|
|
|
|||||
работает конструктор |
|
|
|
|
|
|
||
выводится массив размерностью 10 |
|
|
|
|
||||
0 0 0 0 0 0 0 0 0 0 |
|
|
|
|
|
|
||
работает конструктор |
|
|
|
|
|
|
||
|
|
|
|
|
И |
|||
выводится массив размерностью 5 |
|
|
|
|||||
0 0 0 0 0 |
|
|
|
|
|
|
||
|
|
|
|
|
У |
|
||
работает деструктор |
|
|
|
|
|
|||
работает деструктор |
|
|
|
|
|
|||
|
|
|
Г |
|
|
|||
В данном примере в инструкции: |
|
|
|
|||||
Б |
|
|
|
|||||
array obj(10); |
|
|
|
|
|
|
||
|
|
|
|
|
|
|
||
определяется объект obj и для его создания (и иници лизации) вызывается кон- |
||||||||
структор array(int). Далее в инструкции: |
таobj |
|
|
|
|
|||
print(obj); |
|
|
е |
|
|
|
|
|
// вывод содержимого объ |
|
|
|
|
||||
выводится содержимое объекта obj, используякfriend-функцию print(). При вы- |
||||||||
полнении инструкции: |
т |
|
|
|
|
|
||
print(5); |
|
о |
|
|
|
|
|
|
// преобраз вание 5 в array и вывод |
|
|
|
|
компилятором не находится функция print(int) и выполняется проверка на на- личие в классе array конструкт ра, способного выполнить преобразование в
объект класса array. Так как в классе array имеется конструктор array(int), а точ- |
|
|
л |
нее, просто конструктор с одн м параметром, то такое преобразование возмож- |
|
но (создается временныйиобъект, содержащий массив из пяти чисел). |
|
В некоторых с учаях такие преобразования являются нежелательными |
|
и |
|
или, возможно, приводящими к ошибке. В С++ имеется ключевое слово explicit |
|
Б |
|
для подавленбя неявных преобразований. Конструктор, объявленный как explicit:
explicit array(int = 1);
не может быть использован для неявного преобразования. В этом случае ком- пилятором (в частности Microosft C++) будет выдано сообщение об ошибке:
Compiling...
error C2664: 'print' : cannot convert parameter 1 from 'const int' to 'const class array &' Reason: cannot convert from 'const int' to 'const class array'
No constructor could take the source type, or constructor overload resolution was ambiguous
34
Если необходимо при использовании explicit-конструктора все же создать массив и передать его в функцию print, то надо использовать инструкцию
print(array(5));
Правильно сконструировав классы, можно достичь, чтобы создание объ- ектов было разрешено, а нежелательные неявные преобразования типов запре- щены. Рассмотрим пример, в котором целочисленный размер массива может быть передан в качестве параметра конструктору и недопустимы преобразова- ния целых чисел во временный объект.
|
class array |
|
|
|
|
// |
класс-массив целых чисел |
||||||
|
{ public: |
|
|
|
|
|
|
|
|
|
|||
|
|
class array_size |
|
|
// |
класс-размер массива |
|||||||
|
|
{ public: |
|
|
|
|
|
|
|
|
Р |
||
|
|
|
array_size(int _kl):kl(_kl){} |
|
У |
||||||||
|
|
|
int size() const { return kl;} |
Г |
И |
||||||||
|
|
private: |
|
|
|
|
|
||||||
|
|
}; |
int kl; |
|
|
|
|
|
Б |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
||
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
array(int n,int m) |
|
|
а |
|
|
|
|||||
|
|
} |
|
|
|
|
|
|
|
|
|
||
|
|
{ this->n=n; |
|
|
к |
|
|
|
|
||||
|
|
ms=new int[this->n]; |
|
|
|
|
|
|
|||||
|
|
for(int i=0;i<this->n;i++) ms[i]=m; |
|
|
|
|
|||||||
|
|
|
|
|
|
|
е |
|
|
|
|
|
|
|
|
|
|
|
|
т |
|
|
|
|
|
|
|
|
|
array(array_size |
size) |
|
|
|
|
|
|
||||
|
|
{ n=_size.size(); |
|
|
|
|
|
|
|
||||
|
|
} |
ms=new int[n]; |
|
|
|
|
|
|
|
|||
|
|
|
и |
|
|
|
|
|
|
|
|
||
|
|
|
|
|
|
|
|
|
|
|
|
||
|
}; |
~array(){ delete [] ms;} |
|
|
|
|
|
|
|||||
|
л |
о |
|
|
|
|
|
|
|
||||
|
|
private: |
|
|
|
|
|
|
|
||||
|
|
int n; |
|
|
|
|
|
|
|
|
|
||
|
б |
|
|
|
|
|
|
|
|
|
|
||
|
|
int *ms; |
|
|
|
|
|
|
|
|
|
||
и |
|
|
|
|
|
|
|
|
|
|
|
||
Б |
main() |
|
|
|
|
|
|
|
|
|
|
||
{ |
array a(1); |
|
|
|
|
|
|
|
|
|
|||
|
|
|
|
|
|
|
|
|
|
. . .
};
Компилятор для объекта a генерирует вызов конструктора array(int). Но такого конструктора не существует. Компиляторы могут преобразовать аргу- мент типа int во временный объект array_size, поскольку в классе array_size име- ется конструктор с одним параметром. Это обеспечивает успешное создание объекта.
35