- •Объектно-ориентированное программирование:
- •Содержание
- •Новый тип данных – класс
- •1.2. Доступность компонентов класса
- •1.3. Конструктор и деструктор
- •1.4.4. Статические члены класса
- •1.4. Компоненты-данные и компоненты-функции
- •1.4.1. Данные – члены класса
- •1.4.2. Функции – члены класса
- •1.4.3. Константные компоненты-функции
- •Указатели на компоненты класса
- •1.5.1. Указатели на компоненты-данные
- •1.5.2.Указатели на компоненты-функции
- •1.6. Указатель this
- •1.7. Друзья классов
- •7.1. Дружественная функция
- •1.7.2. Дружественный класс
- •1.8. Определение классов и методов классов
- •2. Наследование
- •2.1. Определение производного класса
- •2.2. Конструкторы и деструкторы производных классов
- •2.3. Виртуальные функции
- •2.4. Абстрактные классы
- •2.5. Включение объектов
- •2.6. Включение и наследование
- •2.7. Множественное наследование
- •2.8. Локальные и вложенные классы
- •2.9. Пример программы для Microsoft Visual Studio
- •2.10. Упражнения
- •3. Перегрузка операций
- •3.1. Перегрузка унарных операций
- •3.2. Перегрузка бинарных операций
- •3.4. Перегрузка операции вызова функции
- •3.5. Перегрузка операции присваивания
- •3.6. Перегрузка операции new
- •3.7. Перегрузка операции delete.
- •3.8. Основные правила перегрузки операций
- •3.9. Примеры программ
- •4. Шаблоны функций и классов
- •4.1. Шаблоны функций
- •4.2. Шаблоны классов
- •4.3. Компонентные функции параметризованных классов
- •4.4. Примеры программ.
- •5. Обработка исключительных ситуаций
- •5.2. Получение дополнительной информации об исключении
- •5.3. Определение типа исключения
- •5.4. Иерархия исключений
- •5.5. Спецификация функций, обрабатывающих исключения
- •6. Потоковые классы
- •6.1. Библиотека потоковых классов
- •6.3. Стандартные потоки ввода-вывода
- •6.4. Форматирование
- •6.5. Манипуляторы
- •6.6. Ввод-вывод объектов пользовательских классов
- •6.7. Определение пользовательских манипуляторов
- •6.8. Пользовательские манипуляторы с параметрами
- •6.9. Использование макросов для создания манипуляторов
- •6.10. Состояние потока
- •6.11. Неформатированный ввод-вывод
- •6.12. Файловый ввод-вывод
- •7.1. Пространство имен
- •7.2. Динамическая идентификация типов
- •7.3. Безопасное приведение типа
- •8. Стандартная библиотека шаблонов
- •8.1. Введение в stl
- •Парадигму обобщенного программирования можно сформулировать следующим образом:
- •8.2. Итераторы
- •8.3. Классы-контейнеры
- •Iterator find(const key_type&k)
- •Iterator lower_bound(const key_type&k)
- •Iterator upper_bound(const key_type&k)
- •8.4. Контейнер vector
- •8.5. Многомерные массивы
- •8.6. Ассоциативные контейнеры
- •8.7. Объекты-функции
- •8.8. Алгоритмы
- •I. Немодифицирующие операции
- •II. Модифицирующие операции
- •III. Сортировка
- •IV. Работа с множествами
- •V. Минимумы и максимумы
- •VI. Перестановки
4.3. Компонентные функции параметризованных классов
Реализация компонентной функции шаблона класса, которая находится вне определения шаблона класса, должна включать дополнительно следующие два элемента:
Определение должно начинаться с ключевого слова template, за которым следует такой же список_параметров_типов в угловых скобках, какой указан в определении шаблона класса.
За именем_класса, предшествующим операции области видимости (::), должен следовать список_имен_параметров шаблона.
template <список_типов> тип_возвр_значения
имя_класса<список_имен_параметров>::
имя_функции(список_параметров)
{тело функции };
Пример 4.3.1.
template <class A,class B> class myclass
{A x;
B y;
public:
A func();
};
template <class A,class B> A myclass<A,B>: : func()
{return A;}
Пример 4.3.2. «Защищенный» массив.
В С++ во время выполнения можно выйти за границу массива без генерации сообщения об ошибке. Хотя эта возможность позволяет генерировать исключительно быстрый исполняемый код, но одновременно служит источником ошибок. Решить эту проблему можно, если создать класс, который содержит массив, и разрешить доступ к массиву через перегруженную операцию []. В функции operator[]() можно перехватывать индекс, выходящий за рамки диапазона массива.
# include <iostream.h>
# include <stdlib.h>
template <class ARRAY> class array
{ARRAY *a;
int length;
public:
array(int size);
~array(){delete[]a;}
ARRAY& operator[](int i);
};
template <class ARRAY> array<ARRAY>: : array(int size)
{register int i;
length=size;
a=new ARRAY[size];
//Проверка, распределена ли память?
if(!a){cout<<”Ошибка!”; exit(1);}
for(i=0;i<size;i++) a[i]=0;
}
template <class ARRAY> ARRAY& array<ARRAY>: : operator[](int i)
{
//Проверка, не вышел ли индекс за границы
if((i<0)||(i>length-1)){cout<<”Ошибка!”; exit(1);}
return a[i];
}
void main()
{array<int> masint(20);
array<double> masdouble(10);
int i;
for(i=0;i<20;i++){masint[i]=i; cout<<masint[i]<<” ”;}
cout<<endl;
for(i=0;i<10;i++){masdouble[i]=(double)i*3.14; cout<<masdouble[i]<<” “;}
cout<<endl;
masint[45]=100; //ошибка, недопустимый индекс
array<char> C(5);
array<int> X(5);
for(i=0;i<5;i++){X[i]=i; C[i]=’A’+i;}
for(i=0;i<5;i++) cout<<X[i]<<C[i]<<’ ’;
}
В списке параметров шаблона могут присутствовать формальные параметры, тип которых фиксирован.
Пример 4.3.3. Размер массива задается по умолчанию
#include<iostream.h>
template <class ARRAY, int size=64> class array
{
ARRAY *a;
int len;
public:
array(){len=size; a=new ARRAY[len];}
int setlen(){return len;}
};
int main()
{
array<int,5> x; //создается массив типа int размером 5
cout<<x.setlen()<<endl; //будет выведено 5
array<int> y; //создается массив типа int размером 64
cout<<y.setlen()<<endl; //будет выведено 64
return 0;
}
Этот пример показывает возможность использования для создания объекта конструктора без параметров с заданием параметров объекта через параметры шаблона.
4.4. Примеры программ.
Задание: написать программу, использующую так называемый «умный» указатель. «Интеллектуальный» или «умный» указатель– это указатель, который автоматически уничтожает объект, когда уничтожается последняя ссылка на него, т.е. память освобождается, когда на объект нет больше ссылок.
Создайте и выполните в Microsoft Visual C++ следующую программу.
#include<iostream> //Для поддержки нового типа string
#include <string> //Тип string библиотеки STL
using namespace std; //использовать пространство имен std
//класс Ref хранит исходный указатель и счетчик ссылок
template<class T> struct Ref
{
T* realPtr; //исходный указатель на объект
int count; //счетчик ссылок
};
//шаблон класса «умного» указателя
template<class T> class SmartPtr
{
Ref<T>* refPtr;
public:
SmartPtr(T*ptr=NULL);
SmartPtr(const SmartPtr& s);
~SmartPtr();
//Перегруженная операция присваивания
SmartPtr& operator=(const SmartPtr& s);
// Перегруженная операция доступа к членам класса через ука//затель
T* operator->()const;
// Перегруженная операция разыменовывания
T& operator*()const;
// Перегруженная операция преобразования типа
operator int(){return refPtr->count;}
};
template<class T> SmartPtr<T>::SmartPtr(T*ptr)
{
if(!ptr)refPtr=NULL;
else
{
refPtr=new Ref<T>;
refPtr->realPtr=ptr;
refPtr->count=1;
}
}
template<class T> SmartPtr<T>::~SmartPtr()
{
if(refPtr)
{
refPtr->count--;
if(refPtr->count<=0)//если ссылок нет, освободить память
{
delete refPtr->realPtr;
delete refPtr;
refPtr=NULL;
}
}
}
template<class T> T* SmartPtr<T>::operator->()const
{
if(refPtr)return (refPtr->realPtr);
else return NULL;
}
template<class T> T& SmartPtr<T>::operator*()const
{
if(refPtr)return *(refPtr->realPtr);
else throw ; //генерировать исключение
}
template<class T> SmartPtr<T>::SmartPtr(const SmartPtr& s)
{
refPtr=s.refPtr;
if(refPtr)refPtr->count++;
}
/*
При выполнении операции присваивания прежде всего нужно отсоединиться от имеющегося объекта, а затем присоединиться к новому, подобно тому, как это сделано в конструкторе копирования
*/
template<class T> SmartPtr<T>& SmartPtr<T>::operator=(const SmartPtr& s)
{
if(refPtr)
{
refPtr->count--;
if(refPtr->count<=0) //если ссылок нет, освободить память
{
delete refPtr->realPtr;
delete refPtr;
}
}
refPtr=s.refPtr;
if(refPtr)refPtr->count++;//если есть ссылки
return *this;
}
//Типы объектов, с которыми будет работать указатель SmartPtr
struct A
{
int x,y;
double d;
string s;
};
struct B
{
int x,y,z;
string s1,s2;
};
int main()
{
SmartPtr<A> aPtr(new A);
//С объектами класса SmartPtr можно обращаться как с обычными указателями на объект типа T
{//начало блока
(aPtr->x)=5;
cout<<"aPtr->x="<<aPtr->x<<endl;
(*aPtr).y=3;
cout<<"aPtr->y="<<aPtr->y<<endl;
(aPtr->d)=45.67;
cout<<"aPtr->d="<<aPtr->d<<endl;
(aPtr->s)="new string";
cout<<"aPtr->s="<<aPtr->s<<endl;
cout<<"count="<<(int)aPtr<<endl;//Количество ссылок=1;
{//Объекты SmartPtr размещаются в автоматической памяти
SmartPtr<A> aPtr1=aPtr;
cout<<"count="<<(int)aPtr<<endl;// количество ссылок=2;
SmartPtr<A> aPtr2=aPtr;
cout<<"count="<<(int)aPtr<<endl;//количество ссылок=3;
SmartPtr<A> aPtr3;
aPtr3=aPtr;
cout<<"count="<<(int)aPtr<<endl;// количество ссылок=4;
(aPtr2->x)=15; //Изменяется для всех связанных объектов
cout<<"aPtr->x="<<aPtr->x<<endl;//Выводится 15
} //конец блока - освобождаются 3 ссылки на объект
cout<<"count="<<(int)aPtr<<endl;//Количество ссылок=1;
//Теперь будем размещать объекты в динамический памяти
SmartPtr<A>*p,*p1,*p2;
p=new SmartPtr<A>(new A);
(*p)->x=155;
cout<<"count="<<((int)(*p))<<endl;//Количество ссылок =1;
p1=new SmartPtr<A>(*p);
cout<<"count="<<((int)(*p))<<endl;//Количество ссылок =2
p2=new SmartPtr<A>(new A);
//количество ссылок не изменилось, т.к. p2 указывает на новый //объект
cout<<"count="<<((int)(*p))<<endl;//Количество ссылок =2
(*p2)=(*p); //теперь p2 указывает на тот же объект
cout<<"count="<<((int)(*p))<<endl;//Количество ссылок =3
delete p;
cout<<"count="<<((int)(*p1))<<endl;//Количество ссылок =2
delete p1;
cout<<"count="<<((int)(*p2))<<endl;//Количество ссылок =1
cout<<(*p2)->x<<endl;
//Будем обрабатывать ошибки через исключения
//Контролируемый блок
try
{
delete p2;
//Здесь будет ошибка, т.к. ссылок больше нет и память осво - //бождена
cout<<"count="<<(int)(*p2)<<endl;
}
catch (...){cout<<"No reference!"<<endl;}
// «Умный» указатель можно использовать для
//объектов любого типа
//Создаем указатели на объект типа B
SmartPtr<B> bPtr(new B); //Количество ссылок =1
{
SmartPtr<B> bPtr1=bPtr; //Количество ссылок =2
SmartPtr<B> bPtr2=bPtr; //Количество ссылок =3
}//Здесь две ссылки освобождаются
cout<<"count="<<(int)bPtr<<endl; //Количество ссылок =1
return 0;
}
