- •Содержание
- •ВВЕДЕНИЕ
- •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. Полосы прокрутки
- •Литература
else |
|
{ update(odd,even); |
// создание odd модели из even модели |
pr_state(odd); |
// вывод сгенерированной модели odd |
dele(even); |
// удаление модели even |
} |
|
} |
|
return 0; |
|
} |
|
4.4. Виртуальные деструкторы |
Р |
|
|
Виртуальные деструкторы необходимы при использовании указателей на |
|
|
И |
базовый класс при выделении памяти под динамически создаваемые объекты |
производных классов. Это обусловлено тем, что если объект уничтожается яв-
класса, совпадающего с типом указателя на уничтожаемыйУобъект. При этом не учитывается тип объекта, на который указывает данный указатель.
но, например, используя операцию delete, то вызывается деструктор только
рукторы производных классов становятся такжеГвиртуальными. При этом если будет выполняться явное уничтожение объекта производного класса для указа-
В случае объявления деструктора базового класса виртуальным, все дест-
теля на базовый класс, то вначале вызыв ется деструктор производного класса, |
|
а затем вверх по иерархии до дестру т |
Б |
б зового класса. |
деструкторов и в случае их отсу ствия на примере программы, вычисляющей
ора Рассмотрим отличия в рабокпрогр ммы при использовании виртуальных
|
|
|
т |
|
площади некоторых фигур (круг, прямоугольник). |
||||
|
#include <iostream> |
те |
||
|
|
о |
|
|
|
#include <iomanip> |
|
||
|
using namespace std; |
|
||
|
#include <string.h> |
|
||
|
л |
|
|
// базовый класс «фигура» |
|
class Shape |
|
|
|
|
б |
|
|
|
|
{protected:и |
|
// площадь фигуры |
|
|
float s; |
|
|
|
и |
|
|
|
|
|
public: |
|
|
|
Б |
Shape(char *fig) : s(0) |
{ cout << "конструктор класса Shape (фигура "<< fig <<')'<< endl;} virtual ~Shape()
{ cout << "деструктор класса Shape" << endl;} void virtual print()
{cout<<s<<endl;} void virtual area()=0;
};
class Circle : public Shape // производный класс «круг»
{ int r;
91
public:
Circle(char *name,int r): Shape(name)
{cout << "конструктор класса Circle "<<endl; this->r=r;
}
~Circle()
{ cout << "деструктор класса Circle " << endl;} void area();
}; |
|
Р |
class Bar : public Shape // производный класс «прямоугольник» |
||
{ int n,m; |
|
|
public: |
|
|
Bar(char *name,int n,int m): Shape(name) |
|
|
И |
||
{ cout << "конструктор класса Bar "<<endl; |
ГУ |
|
this->n=n; |
|
|
this->m=m; |
|
|
} |
Б |
|
~Bar() |
|
|
{ cout << "деструктор класса Bar " << endl;} |
|
|
void area(); |
|
}; |
|
|
|
|
|
|
|
|
а |
void Circle::area() |
|
|
|
|
|
||||
|
|
|
|
к |
|||||
{ s=r*r*3.14; |
|
|
|
|
|
||||
|
cout<<"Площадь круга = "; |
е |
|
||||||
|
this->print(); |
|
|
|
|
||||
} |
|
|
т |
|
|
||||
|
|
|
|
|
|
|
|||
void Bar::area() |
|
|
|
|
|||||
{ s=n*m; |
|
|
|
о |
|
|
|
||
|
cout<<"П ощадь прямоугольника = "; |
|
|||||||
|
this->print(); |
и |
|
|
|
|
|||
} |
|
|
л |
|
|
|
|
|
|
int main() |
|
|
|
|
|
|
|
||
{ |
|
б |
|
|
|
|
|
|
|
Shape *fg1,*fg2; |
|
|
|
|
|
||||
|
fg1=newиCircle("Круг",2); |
|
|
|
|||||
|
fg2=new Bar("Прямоугольник",3,4); |
|
|||||||
Б |
|
|
|
|
|
|
|
|
fg1->area(); fg2->area(); delete fg1; delete fg2; return 0;
}
92
Результат работы программы: конструктор класса Shape (фигура Circle) конструктор класса Circle
конструктор класса Shape (фигура Bar) конструктор класса Bar
площадь круга =12.56
площадь прямоугольника =12 |
|
|
|
деструктор класса Circle |
|
|
|
деструктор класса Shape |
|
|
|
деструктор класса Bar |
|
|
|
деструктор класса Shape |
|
|
|
В случае если деструктор базового класса не являлся бы виртуальным, то |
|||
при удалении объектов производных классов осуществлялся Рбы вызов только |
|||
|
У |
||
деструктора класса соответствующего ему типа , т.е. базового класса (класса, |
|||
Г |
И |
||
для которого объявлен соответствующий указатель). |
|
|
|
Если в классе имеются виртуальные функции, |
то желательно объявлять |
деструктор этого класса также виртуальным, даже если этого не требуется. Это может предотвратить возможные ошибки.
4.5. Множественное наследов ние |
Б |
||||
В языке С++ имеется возможность образовывать производный класс от |
|||||
нескольких базовых классов. Общая фор |
|
множественного наследования име- |
|||
ет вид |
|
|
ма |
|
|
|
к |
|
|
||
|
|
|
|
||
class имя_произв класса : имя_базового_кл 1,…,имя_базового_кл N |
|||||
{ содержимое класса |
е |
|
|
|
|
}; |
|
|
|
|
|
Иерархическая с рук ура, в которой производный класс наследует от не- |
|||||
скольких базовых класств, называется множественным наследованием. В этом |
|||||
случае производныйокласс, имея собственные компоненты, имеет доступ к |
|||||
protected- и public-компонентам базовых классов. |
|||||
|
и |
|
|
|
|
Конструкторы базовых классов при создании объекта производного клас- |
|||||
са вызываютсялв том порядке, в котором они указаны в списке при объявлении |
|||||
зводного класса. |
|
|
|
|
|
б |
|
|
|
|
|
При применении множественного наследования возможно возникновение |
прои Бнескольких конфликтных ситуаций. Первая − конфликт имен методов или ат-
рибутов нескольких базовых классов: class A
{public: void fun(){} };
class B
{public: void fun(){} };
93
class C : public A, public B
{ };
int main() |
|
{ C *c=new C; |
|
c->fun(); |
// error C::f' is ambiguous |
return 0; |
|
}
При таком вызове функции fun() компилятор не может определить, к ка- кой из двух функций классов A и B выполняется обращение. Неоднозначность можно устранить, явно указав, какому из базовых классов принадлежит вызы-
ваемая функция: |
|
|
|
|
|
|
|
|
Р |
c->A:: fun(); |
|
или |
c->B::fun(); |
|
|
|
|||
|
|
|
|
|
|
|
|
|
|
Вторая проблема возникает при многократном включении некоторого |
|||||||||
базового класса: |
|
|
|
|
|
|
|
И |
|
#include <iostream> |
|
|
|
|
|
|
|||
|
|
|
|
|
У |
|
|||
using namespace std; |
|
|
|
|
|
|
|||
#include <string.h> |
|
|
|
|
|
|
|||
|
|
|
|
Г |
|
|
|||
class A |
|
|
|
|
|
|
|
||
|
|
|
// базовый класс I уровня |
|
|
|
|||
{ char naz[20]; |
|
// |
название фирмы |
Б |
|
|
|
||
public: |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
A(char *NAZ) {strcmp(naz,NAZ);} |
|
|
|
|
|||||
~A() {cout << "деструктор класса А"а<< endl;} |
|
|
|
||||||
void a_prnt() {cout << naz << endl;}к |
|
|
|
|
|||||
}; |
|
|
|
|
е |
|
|
|
|
class B1 : public A |
|
|
|
|
|
|
|
||
|
|
// производный класс (1 Базовый II уровня) |
|||||||
{ protected: |
|
|
|
т |
|
|
|
|
|
long tn; |
|
|
|
|
|
|
|||
int nom; |
|
|
|
|
|
|
|||
|
о |
|
|
|
|
|
|||
public: |
|
|
|
|
|
|
|
||
B1(char *NAZ,longиTN,int NOM): A(NAZ),tn(TN),nom(NOM) {}; |
|||||||||
~B1() {cout << "деструктор класса В1" << endl;} |
|
|
|
||||||
void b1 |
л |
|
|
|
|
|
|
|
|
prnt() |
|
|
|
|
|
|
|
|
|
{ A::aбprnt(); |
|
|
|
|
|
|
|
|
|
cout << " таб. N " << tn <<" подразделение = " << nom <<endl; |
|||||||||
и |
|
|
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
}; |
|
|
|
|
|
|
|
|
|
Б |
|
|
|
// производный класс (2 Базовый II уровня) |
|||||
class B2 : public A |
|
|
|||||||
{ protected: |
|
|
|
|
|
|
|
|
|
double |
zp; |
|
|
|
|
|
|
|
|
public: |
|
|
|
|
|
|
|
|
|
B2(char *NAZ,double ZP): A(NAZ),zp(ZP) {}; ~B2(){cout << "деструктор класса В2" << endl;}
94
void b2_prnt()
{A::a_prnt();
cout << " зар/плата = " << zp << endl;
}
};
class C : public B1, public B2 // производный класс ( III уровня) { char *fam;
public:
C(char *FAM,char *NAZ,long TN,int NOM,double ZP) : B1(NAZ,TN,NOM), B2(NAZ,ZP)
|
{ |
fam = new char[strlen(FAM)+1] |
|
|
|
Р |
||||||
|
}; |
strcpy(fam,FAM); |
|
|
|
|
|
|
||||
|
|
|
|
|
|
|
|
|
|
|
|
|
|
~C() {cout << "деструктор класса С" << endl;} |
|
||||||||||
|
void c_prnt() |
|
|
|
|
|
|
И |
||||
|
|
|
|
|
|
У |
|
|||||
|
{ B1::b1_prnt(); |
|
|
|
|
|
|
|||||
|
|
B2::b2_prnt(); |
|
|
|
|
|
|
||||
|
|
|
|
|
|
Г |
|
|
||||
|
|
cout << " фамилия " << fam<<endl; |
|
|
||||||||
}; |
} |
|
|
|
|
|
|
Б |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|||
|
|
|
|
|
|
а |
|
|
|
|
||
int main() |
|
|
|
|
|
|
|
|
||||
|
|
|
|
|
|
|
|
|
|
|
||
{ C cc("Иванов","мастра",1234,2,555.6),*pt=&cc; |
|
|
||||||||||
// |
cc.a_prnt(); |
ошибка |
к |
|
|
|
|
|
||||
'C::a_prnt' is ambiguous |
|
|
||||||||||
// |
pt->a_prnt(); |
|
е |
|
|
|
|
|
|
|||
|
cc.b1_prnt(); |
|
|
|
|
|
|
|
||||
|
т |
|
|
|
|
|
|
|
||||
|
pt->b1_prnt(); |
|
|
|
|
|
|
|
||||
|
cc.b2_prnt(); |
|
|
|
|
|
|
|
||||
|
pt->b2 prnt(); |
|
|
|
|
|
|
|
|
|
||
|
|
|
о |
|
|
|
|
|
|
|
|
|
|
cc.c prnt(); |
|
|
|
|
|
|
|
|
|
||
|
pt->c |
и |
|
|
|
|
|
|
|
|
|
|
|
prnt(); |
|
|
|
|
|
|
|
|
|
||
|
return 0; |
|
|
|
|
|
|
|
|
|
|
|
} |
л |
|
|
|
|
|
|
|
|
|
|
|
б |
|
|
|
|
|
|
|
|
|
|
|
|
В приведенном примере производный класс С имеет по цепочке два оди- |
||||||||||||
наковыхибазовых класса А (A<-B1<-C и A<-B2<-C), для каждого базового клас- |
||||||||||||
са А строится свой объект (рис. 3, 4). Таким образом, вызов функции |
||||||||||||
Б cc.a_prnt(); |
|
|
|
|
|
|
|
|
|
|
||
pt->a_prnt(); |
|
|
|
|
|
|
|
|
|
|
некорректен, так как неизвестно, какую из двух функций (какого из двух клас- сов А) требуется вызвать.
95