280 likes | 433 Views
C++ templates. Синтаксис. template < typename T > void f( const T& value) { std:: cout << "Value is: " << value << std:: endl ; } template < typename T, size_t n > struct Array { T data[n]; };. Примеры использования. template < typename T > class auto_ptr {
E N D
Синтаксис template < typenameT > void f(const T& value) { std::cout << "Value is: " << value << std::endl; } template < typename T, size_t n > structArray { T data[n]; };
Примеры использования template < typenameT > class auto_ptr { typedefauto_ptrMyType; T* _ptr; auto_ptr(const MyType& other); MyType& operator = (const MyType& other); public: auto_ptr(T* const ptr) : _ptr(ptr) { } ~auto_ptr() { delete _ptr; } T& operator *() const { return *_ptr; } T* operator ->() const { return _ptr; } };
Примеры использования structA { inta; }; intmain() { auto_ptr<A> p_a = new A; (*p_a).a = 9; inti = p_a->a; return 0; }
smart pointers • std::auto_ptr – лучше не использовать • _com_ptr_t – для COM-интерфейсов • boost::scoped_ptr – некопируемый • boost::shared_ptr – копируемый, использует подсчёт ссылок • boost::weak_ptr – решает проблему «кольцевых ссылок»
boost:: scoped_ptr template<class T> class scoped_ptr// noncopyable { T * ptr; scoped_ptr(scoped_ptrconst &); // prohibited scoped_ptr & operator=(scoped_ptrconst &); // prohibited void operator==( scoped_ptrconst& ) const; // prohibited void operator!=( scoped_ptrconst& ) const; // prohibited public: explicit scoped_ptr(T * p = 0); // never throws explicit scoped_ptr(std::auto_ptr<T> p); // never throws ~scoped_ptr(); // never throws T & operator*() const; // never throws T * operator->() const; // never throws operator bool () const; // never throws bool operator! () const; // never throws T * get() const; // never throws void reset(T * p = 0); // never throws void swap(scoped_ptr & b); // never throws };
std::auto_ptr template < class _Ty > class auto_ptr { public: template<class _Other> operator auto_ptr<_Other>() { return (auto_ptr<_Other>(*this)); } template<class _Other> auto_ptr<_Ty>& operator = (auto_ptr<_Other>& _Right) { reset(_Right.release()); return (*this); } template<class _Other> auto_ptr(auto_ptr<_Other>& _Right) : _Myptr(_Right.release()) {} _Ty *release() { _Ty *_Tmp = (_Ty *)_Myptr; _Myptr = 0; return (_Tmp); } ... };
Недостатки глобальных переменных • Объекты создаются всегда, даже если они не используются • Порядок инициализации в общем случае неизвестен • Объекты не разрушаются до завершения программы • Проблемы с исключениями в конструкторах объектов
Singleton template < typename T > class Singleton { public: static T& Instance() { static T instance; return instance; } static const T& ConstInstance() { return const_cast<const T&>(Instance()); } };
Singleton class A { private: friend class Singleton<A>; A() {} public: inta; }; intmain() { Singleton<A>::Instance().a = 3; std::cout << Singleton<A>::ConstInstance().a << std::endl; return 0; }
Частичная специализация шаблонов template < typename T > T MaxValue(); template < > int MaxValue<int>() { return INT_MAX; } template < > float MaxValue<float>() { return FLT_MAX; } int main() { std::cout << "Max int value = " << MaxValue<int>() << std::endl; std::cout << "Max float value = " << MaxValue<float>() << std::endl; return 0; }
Compile time check template <boolb> structCompileTimeError; template <> structCompileTimeError<true>{}; CompileTimeError<2 == 4> ERROR_assert_failed; #define STATIC_CHECK(expr, msg) \ { CompileTimeError<expr> ERROR_##msg; (void)ERROR_##msg; } intmain() { STATIC_CHECK(2 == 5, assert_failed) return 0; }
Шаблонные шаблонные параметры template < typenameT, template <typename> class Creator = NewCreator > class Singleton { public: typedefT* InstancePtr; private: static InstancePtr _instancePtr; static void Destroy(void) { if(_instancePtr != NULL) Creator<T>::Destroy(_instancePtr); } public: static T& Instance() { if(_instancePtr == NULL) { _instancePtr = Creator<T>::Create(); atexit(&Singleton::Destroy); } return *_instancePtr; } }; template < typenameT, template <typename> class C> typenameSingleton<T, C>::InstancePtr Singleton<T, C>::_instancePtr = NULL;
Шаблонные шаблонные параметры template <class T> structMallocCreator { static T* Create() { void* p = std::malloc(sizeof(T)); if (!p) return 0; return new(p) T; } static void Destroy(T* p) { p->~T(); std::free(p); } }; template <class T> structNewCreator { static T* Create() { return new T; } static void Destroy(T* p) { delete p; } };
Недостатки указателей на функции • Нет информации о типах аргументов и возвращаемого значения • Указатель на функцию необходимо перед вызовом проверять на NULL • Есть возможность привести указатель на функцию к любому указателю • Разный синтаксис вызова для указателей на функции и указателей на методы
Функторы, простая реализация template < typenameSignature > class function; template < typenameR, typenameP > class function < R (P) > { typedeffunction MyType; typedefR (*Signature)(P); Signature _func; MyType& operator = (const MyType&); public: function(Signature func) : _func(func) { } function(const MyType& other): _func(other._func) { } R operator () (P p) { return _func(p); } };
Функторы, простая реализация template <typenameObjType, typenameR, typenameP1, typenameP2> class function < R (ObjType::*) (P1, P2) > { typedeffunction MyType; typedefR (ObjType::*Signature)(P1, P2); Signature _func; MyType& operator = (const MyType&); public: function(Signature func) : _func(func) { } function(const MyType& other): _func(other._func) { } R operator () (ObjType& obj, P1 p1, P2 p2) { return (obj.*_func)(p1, p2); } };
Функторы, простая реализация structA { char C; A(char c = ‘X') : C(c) { } void member_func(double i, boolb) { if (b) std::cout << C << i << std::endl; } }; intmain() { A a; function< void (A::*)(double, bool) > f(&A::member_func); f(a, 0.5, true); function < int (int) > f2(&abs); std::cout << f2(-123) << std::endl; return 0; }
Функторы STL intmain() { A a; std::mem_fun1_t< void, A, double > f = std::mem_fun(&A::member_func); f(&a, 0.5); std::mem_fun1_ref_t < void, A, double > f_ref = std::mem_fun_ref(&A::member_func); f_ref(a, 0.7); std::pointer_to_unary_function<int, int> f2 = std::ptr_fun(&abs); std::cout << f2(-123) << std::endl; return 0; }
Недостатки функторов STL • Уродливый синтаксис • Отсутствие функторов с большим количеством параметров • Отсутствует возможность инициализации функтора с сигнатурой func1 указателем на func2: struct B : public A{}; void func1(B*); void func2(A*);
boost::function intmain() { A a; boost::function < void (A&, double) > f = &A::member_func; f(a, 0.5); boost::function < int (int) > f2 = &abs; std::cout << f2(-123) << std::endl; return 0; }
Применение функторов void generate_int(int& i) { i = rand() % 100 - 50; } void print_int(inti) { std::cout << i << " "; } intmain() { std::vector<int> vec(10); std::for_each(vec.begin(), vec.end(), &generate_int); std::for_each(vec.begin(), vec.end(), &print_int); std::cout << std::endl; std::vector<int> out_vec; std::transform(vec.begin(), vec.end(), std::back_inserter(out_vec), &abs); std::for_each(out_vec.begin(), out_vec.end(), &print_int); std::cout << std::endl; return 0; }
Привязывание параметров intmain() { std::vector<int> vec(10); std::for_each(vec.begin(), vec.end(), &generate_int); std::for_each(vec.begin(), vec.end(), &print_int); std::cout << std::endl; vec.erase( std::remove_if(vec.begin(), vec.end(), std::bind1st(std::less<int>(), 10)), vec.end() ); std::for_each(vec.begin(), vec.end(), &print_int); std::cout << std::endl; return 0; }
Недостатки std::bind1st/2nd • Работают только для binary_function • Привязывают только один аргумент • Неудобный синтаксис • Нет возможности привязать ссылку: void inc (int& n, bool) { ++n; } // ... std::bind1st(std::ptr_fun(&inc), i) (true);
boost::bind boolin_range( intmin_val, intmax_val, intval ) { return (val >= min_val) && (val <= max_val); } intmain() { // ... boost::function < bool(int) > pred = boost::bind(&in_range, 5, 15, _1); vec.erase( std::remove_if(vec.begin(), vec.end(), pred), vec.end() ); // ... return 0; }
“Подводные камни”boost::bind class WindowBase { typedefboost::function < void (void) > EventHandler; EventHandler _onPaint; protected: WindowBase(const EventHandler& onPaint) : _onPaint(onPaint) { } // ... }; structMyWindow : public WindowBase { MyWindow() : WindowBase(boost::bind(&MyWindow::OnPaint, this)) { } void OnPaint() { } }; MyWindowCreateMyGreatWindow() { return MyWindow(); } intmain() { MyWindowwnd = CreateMyGreatWindow(); return 0; }
Преимущества использования шаблонов C++ • Шаблоны решают проблему дублирования кода • Зачастую шаблоны позволяют избавиться от динамической диспечеризации и повысить скорость работы приложения • Позволяют отследить большую часть ошибок на этапе компиляции • Использование стратегий позволяет не писать сложные классы с нуля, а собирать их из множества меньших • Можно грабить корованы
Список литературы • Бьерн Страуструп. Язык программирования С++ • Скотт Мейерс. Эффективное использование С++ • Скотт Мейерс. Наиболее эффективное использование С++ • Скотт Мейерс. Эффективное использование STL • Герб Саттер. Решение сложных задач на C++ • Герб Саттер. Новые сложные задачи на C++ • Андрей Александреску. Современное проектирование на C++ • Герб Саттер. Андрей Александреску. Стандарты программирования на С++. 101 правило и рекомендация • Владимир Сорокин. Голубое сало.