Компонентные функции параметризованных классов
Реализация компонентной функции шаблона класса, которая находится вне определения шаблона класса, должна включать дополнительно следующие два элемента: 1. Определение должно начинаться с ключевого слова template, за которым следует такой же список_параметров_типов в угловых скобках, какой указан в определении шаблона класса. 2. За именем_класса, предшествующим операции области видимости (::), должен следовать список_имен_параметров шаблона. 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; } Этот пример показывает возможность использования для создания объекта конструктора без параметров с заданием параметров объекта через параметры шаблона.
Примеры программ. Задание: написать программу, использующую так называемый «умный» указатель. «Интеллектуальный» или «умный» указатель– это указатель, который автоматически уничтожает объект, когда уничтожается последняя ссылка на него, т.е. память освобождается, когда на объект нет больше ссылок. Создайте и выполните в 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; }
Воспользуйтесь поиском по сайту: ©2015 - 2024 megalektsii.ru Все авторские права принадлежат авторам лекционных материалов. Обратная связь с нами...
|