- •Указатели на функции
- •Для вызова функции через указатель:
- •Указатели на функции-члены
- •Чтобы указатель на функцию-член указывал на метод класса SomeClass::some_member_func(int, char*), нужно выполнить следующую инструкцию:
- •22. Экземпляризация классов и функций из шаблонов. Примеры
- •Шаблоны функций и классов
- •На пример список. Список может быть для чего угодно.
- •Построение шаблонов рассмотрим на примере. Необходимо написать функцию расчёта куба для аргумента. (Cub()).
- •Шаблоны классов
- •Шаблон класса представляет собой скелет обобщённого класса. Ситаксис такой:
- •Связные списки и другие структуры
- •23. Передача в функции объектов класса. Решение проблемы вызова конструктора и деструктора при работе локальных функций. Примеры.
Начнем с краткого обзора простых указателей на функции. В С, и С++ в частности, указатель на функцию с именем my_func_ptr , указывающий на функцию, принимающую в качестве аргументов int и char*, и возвращающую float, объявляется так:
float (*my_func_ptr)(int, char*);
// Для большей удобочитаемости, я очень рекомендую использовать typedef.
// Особенно можно запутаться, когда указатель на функцию является аргументом
// функции.
// Тогда бы объявление выглядело так:
typedef float (*MyFuncPtrType)(int, char*);
MyFuncPtrType my_func_ptr;
Заметьте, что различным комбинациям аргументов соответствуют различные типы указателей на функцию. В MSVC, кроме этого, указатели на функцию различаются в зависимости от типа соглашения о вызове (calling conventions): __cdecl, __stdcall, __fastcall. Для того чтобы указатель на функции указывал на вашу функцию, необходимо выполнить следующую конструкцию:
my_func_ptr = some_func;
Для вызова функции через указатель:
(*my_func_ptr)(7, "Arbitrary string");
Разрешается приводить один тип указателя на функцию к другому. Но не разрешается приводить указатель на функцию к указателю на void. Остальные разрешенные операции тривиальны. Указателю на функцию можно присвоить 0 для обозначения нулевого указателя. Доступны многочисленные операторы сравнения (==, !=, <, >, <=, >=), можно также проверить на равенство 0 либо неявным преобразованием к bool. Кроме того, указатель на функцию может быть использован в качестве нетипизированного параметра шаблона. Он в корне отличается от типизированного параметра, а также отличается от интегрального нетипизированного параметра шаблона. При инстанцировании используется имя, а не тип или значение. Именные параметры шаблонов поддерживаются не всеми компиляторами, даже не всеми из тех, которые поддерживают частичную специализацию шаблонов.
Наиболее распространенное применение указателей на функции в С – это использование библиотечных функций, таких как qsort, и обратных (callback) функций в Windows. Кроме того, есть еще много вариантов их применения. Реализация указателей на функции проста: это всего лишь «указатели на код», в них содержится начальный адрес участка ассемблерного кода. Различные типы указателей существуют лишь для уверенности в корректности применяемого соглашения о вызове.
Указатели на функции-члены
В программах на С++, большинство функций являются членами. Это означает, что они являются частью класса. И использовать обычный указатель на функцию в этом случае нельзя. Вместо этого нужно использовать указатель на функцию-член. Указатель на функцию-член класса SomeClass, с теми же аргументами, что и ранее, объявляется следующим образом:
float (SomeClass::*my_memfunc_ptr)(int, char*)
// Для константных функций-членов используется объявление
float (SomeClass::*my_const_memfunc_ptr)(int, char*) const;
Заметьте, что используется специальный оператор ( ::* ), а при объявлении используется класс SomeClass. Указатели на функции-члены имеют очень серьезное ограничение – они могут указывать лишь на функции-члены одного класса. Различным комбинациям аргументов, типам константности и различным классам соответствуют различные указатели на функции-члены. В MSVC , кроме того, указатели различаются по типу соглашения о вызове: __cdecl, __fastcall, __stdcall и __thiscall (__thiscall по умолчанию. Заметим, что документированного квалификатора __thiscall нет, но он иногда появляется в сообщениях об ошибках. Если вы попробуете использовать его явно, то получите сообщение об ошибке, информирующей о том, что использование этого квалификатор зарезервировано для будущих нужд.) При использовании указателей на функции-члены всегда следует использовать typedef, во избежание ошибок и лишних неприятностей.
Чтобы указатель на функцию-член указывал на метод класса SomeClass::some_member_func(int, char*), нужно выполнить следующую инструкцию:
my_memfunc_ptr = &SomeClass::some_member_func;
Многие компиляторы (например, NSVC) разрешат вам пропустить взятие адреса (&), но более соответствующие стандарту (например, GNU G++) потребуют этого. Так что если пишете портируемый код, то не забывайте &. Для вызова метода через указатель на функцию-член нужно предоставить объект SomeClass и воспользоваться специальным оператором (->*). Данный оператор имеет низкий приоритет, так что его следует поместить в скобки:
SomeClass *x = new SomeClass;
(x->*my_memfunc_ptr)(6, "Another arbitrary parameter");
// Также можно использовать оператор .* если класс создан на стеке
SomeClass y;
(y.*my_memfunc_ptr)(15, "Different parameter");
Прошу меня не винить в ужасном синтаксисе – похоже, кто-то из разработчиков С++ любит знаки препинания!
С++ добавил в С три специальных оператора специально для поддержки указателей на функции-члены. ::* используется при объявлении указателя, ->* и .* используются при вызовах методов, на которые указывает указатель. Похоже, очень много внимания было уделено этой неясной и редко используемой части языка (разрешено даже перегрузить оператор ->*, однако, я не представляю, для чего это может вам потребоваться; я только знаю одно применение [Meyers]).
Указатели на функции-члены могут быть установлены в 0, и предоставляют операторы ==, !=, но лишь для указателей на функции-члены одного класса. Любой указатель на функцию-член может быть проверен на равенство 0. В отличие от простых указателей на функции, операции сравнения на неравенство (<,>, <=, >=) недоступны. Как и простые указатели, они могут быть использованы как нетипизированные параметры шаблона.
22. Экземпляризация классов и функций из шаблонов. Примеры
Шаблоны функций и классов
Шаблоны представляют ещё одну реализацию полиморфизма в программах С++. Они позволяют создать универсальный фрагмент кода, а затем использовать его многократно с различными типами данных или различными объектами. С помощью шаблонов можно уменьшить объём и сложность программы.
Шаблоны функций позволяют передавать в качестве аргумента тип переменной. Шаблоны создают семейства функций, определяющее неограниченное множество родственных функций. Экземпляризация— операция создания определённого типа из шаблона. Отдельные классы называются экземплярами шаблона. Параметризованные шаблоны — представляют возможность создания общего класса и для построения конкретных экземпляров передают этому классу в качестве параметров типы данных.
На пример список. Список может быть для чего угодно.
Построение шаблонов рассмотрим на примере. Необходимо написать функцию расчёта куба для аргумента. (Cub()).
#include<iostream.h> #include<math.h>
template <class T> T cub(T x) { return x*x*x;} template <class SwapType> void Swap(SwapType &x,SwapType &y) { SwapType tmp; tmp=x; x=y; y=tmp; } template <class T1,class T2> T1 max(T1 x,T2 y) { if(x>y)return x; else return y; } void main(void) { int i=3; float f=3.12; double x=3.1e2; cout<<"//целое "<<cub(i)<<endl; cout<<"//вещественное "<<cub(f)<<endl; cout<<"//двойной точности "<<cub(x)<<endl; char ch1='a',ch2='z'; cout<<"//1-- "<<ch1<<" 2-- "<<ch2<<endl; Swap(ch1,ch2); cout<<"//переставили "<<endl; cout<<"//1-- "<<ch1<<" 2-- "<<ch2<<endl; int c1=0,c2=9; cout<<"//1-- "<<c1<<" 2-- "<<c2<<endl; Swap(c1,c2); cout<<"//переставили "<<endl; cout<<"//1-- "<<c1<<" 2-- "<<c2<<endl;
cout<<"//max из int "<<c1<<" и int "<<c2<<" = "<<max(c1,c2)<<endl; cout<<"//max из char "<<ch1<<" и char "<<ch2<<" = "<<max(ch1,ch2)<<endl; cout<<"//max из int "<<i<<" и float "<<f<<" = "<<max(f,i)<<endl; }
//целое 27 //вещественное 30.3713 //двойной точности 2.9791e+07 //1-- a 2-- z //переставили //1-- z 2-- a //1-- 0 2-- 9 //переставили //1-- 9 2-- 0 //max из int 9 и int 0 = 9 //max из char z и char a = z //max из int 3 и float 3.12 = 3.12
Шаблоны классов
Шаблон класса представляет собой скелет обобщённого класса. Ситаксис такой:
Template< список_аргументов_шаблона> { //тело класса };
Каждый аргумент в списке является либо объявлением типа float a; либо идентификатором класса class T. Из-за этого определение функции-метода шаблонного класса имеет вид: Template< список_аргументов_шаблона> Тип_результата имя_класса < список_аргументов_шаблона>:: Имя_функции( список_аргументов_функции) { //тело функции. }
объявление объекта шаблонногокласса: имя_класса_шаблона < список_аргументов_шаблона> имя_объекта; Пример использования шаблонов будем рассматривать на создании очереди.
Связные списки и другие структуры
Для работы с массивом необходимо знать его размерность, хотя бы в начале работы программы. Если это данное не известно то можно либо сильно увеличить резервируемую память, либо рисковать ограниченность пространства для данных в массиве. Данную проблему решают связные списки. Связанный список это структура данных, содержащая из взаимосвязанных блоков. Идея состоит в том, что бы создать класс, который поддерживал бы данные определённого типа, среди которых был бы указатель, связанный с другим объектом этого класса.
Существуют три основных вида списков:
Однонаправленные списки (очередь, стек);
Двунаправленные списки (дек и т.д.);
Деревья.