Программирование >>  Обобщенные обратные вызовы 

1 ... 6 7 8 [ 9 ] 10 11 12 ... 84


наследования; во-вторых, при вызове виртуальных функций во внутренних циклах можно заметить определенное уменьшение производительности, поскольку обычно вызов виртуальной функции выполняется посредством указателя, с дополнительным уровнем косвенности, который позволяет компилятору выяснить, какой именно вызов ........функции базового или производного объекта - должен быть выполнен.

Если вы знаете используемые типы во время компиляции, вы можете избежать обоих недостатков полиморфизма времени выполнения: вы можете использовать типы, не связанные наследованием, лишь бы они обеспечивали возможность выполнения ожидаемых операций.

пример 5-1(6): полиморфизм времени компиляции.

class xyzzy { public:

void f С bool someparm = true ) ; void gO ;

void GoToGazeboO ;

... прочие функции ...

class Murgatroyd { publi с:

void fО;

void g( double two = 6.28, double e = 2.71828 ); i nt HeavensToC const Z& ) const; ... прочие функции ...

tempiate<class T> void h( T& t ) {

t.fO; t.gO;

i nt mai n () { xyzzy x; Murgatroyd m; h( X ); hC m );

До тех пор пока объекты хит будут иметь типы, обеспечивающие возможность вызова функций f и g без аргументов, функция h будет корректно работать. В примере 5-1(6) в действительности функции f и g имеют разные сигнатуры в разных классах, а кроме того, эти классы, помимо интересующих нас f и д, имеют разные дополнительные функции. Но все это не оказывает никакого влияния на работу функции h. До тех пор пока функции f и g могут быть вызваны без аргументов, компилятор позволит функции h вызывать f и д. Конечно, при вызове эти функции должны делать нечто осмысленное для функции h!

Таким образом, шаблоны обеспечивают мощный полиморфизм времени компиляции. Неверное использование шаблонов может, конечно, привести к совершенно неудобочитаемым сообщениям об ошибках, но одновременно шаблоны являются одной из наиболее сильных возможностей С+ + .

2. Поясните семантику приведенной функции. Дайте максимально полный ответ и обязательно поясните, почему в ней использованы два параметра шаблона, а не один.

template <с1ass т1, class т2> void constructC т1* р, const т2& value ) { new (р) Tl(value);

Функция construct со:здаст объект в указанном месте в памяти и инициализирует его дани>1м значением. Оператор new, использованный в данном примере, иазьшается



размещающим new (placement new), и вместо выделения памяти для нового объекта он просто помещает его в область памяти, на которую указывает р. Любой объект, создаваемый таким образом, в общем случае должен быть уничтожен явным вызовом деструктора (как показано в задаче 6, вопрос 1), а не путем использования оператора delete.

Итак, зачем же нужны два параметра шаблона? Неужели одного недостаточно для того, чтобы создать копию объекта value? Например, если шаблон construct имеет только один параметр, вам может потребоваться явно указать тип этого параметра при копировании объекта другого типа.

Пример 5-2: шаблон construct с меньшими функциональными

возможностями, и пояснение, почему они меньше.

template <с1ass т1>

void constructC Tl* р, const т1& value ) { new (p) Tl(value);

Считаем, что и pi, и p2 указывают на нетипизированную область памяти.

void f( double* pi, Base* p2 ) { Base b; Derived d;

constructC pi, 2.718 ); OK

constructC p2, b ); OK

constructC pi, 42 ); Ошибка: Tl - double

или int? construct<double>C pi, 42 ); OK constructC p2, d ); Ошибка: Tl - Base

или Derived? construct<Base>C p2, d ); OK

Причина, no которой два вызова помечены как ошибочные, - в неоднозначности. Компилятору недостаточ1ю имеющейся информации для того, чтобы вывести аргумент шаблона, поэтому для корректности кода следует указывать этот аргумент явным образом. Можем ли мы позволить программисту без каких-либо предупреждений построить объект doubl е из целого значения? Вероятно, в худшем случае это повлечет лишь потерю точности. Можем ли мы позволить построить объект типа Base из объекта Deri ved? Пожалуй, да. Если это допустимо, то произойдет срезка объекта Derived {но это может б!ять сделано намеренно).

Итак, если мы хотим позволить программисту выполнять описанные действия без явного указания типов, то нам нужна исходная версия шаблона с двумя независимыми параметрами.



Задача 6. Красота обобщенности.

Часть 2: Достаточно ли универсальности? Сложность: 7

Насколько универсальной в действительности является обобщенная функция? Ответ может зависеть как от ее реализации, так и от ее интерфейса, а отлично разработанный интерфейс может быть сведен на нет простыми (и трудными в обнаружении) ошибками программирования.

Вопрос для профессионала

1. В приведенной функции есть одна малозаметная ловушка, связанная с обобщенностью. В чем она состоит и как лучше всего ее исправить?

template <с1ass т> voi d destroy С т* р ) { р->~т();

template <с1ass Fwdlter>

void destroyC pwdlter first, Fwdlter last ) { whileC fi rst != last ) { destroyC fi rst ); ++fi rst;

2. В чем заключается семантика приведенной далее функции, включая Т1)ебования к Т. Можно ли снять какие-либо из этих требований? Если да, то продемонстрируйте, как именно, и укажите достоинства и недостатки соответствующего решения.

template <с1ass т>

void swapC т& а, т& b ) {

Т temp(a); а = b; b = temp;

Решение

1. в приведенной функции есть одна малозаметная ловушка, связанная с обобщенностью. В чем она состоит и как лучше всего ее исправить?

template <class т> void destroyC т* р ) { Р->~тС);

template <с1ass Fwdlter>

void destroyC Fwdlter first, Fwditer last ) { whileC first != last ) { destroyC first ); ++fi rst;

Шаблон функции destroy предназначен для уничтожения диапазона объектов. Первая версия получает один указатель и вызывает деструктор указываемого объекта. Вторая версия получает диапазон итераторов и итеративно уничтожает объекты в указанном диапазоне.

Здесь есть одна тонкая ловушка, которая не проявлялась ни в одном примере при первоначальном появлении этого кода в книге [SiitterOO]. Небольшая проблема состоит в том, что функция с двумя параметрами destroyCFwdlter, Fwdlter) может получать любой обобщенный итератор, но при работе она вызывает функцию с 0Д1П1М



1 ... 6 7 8 [ 9 ] 10 11 12 ... 84

© 2006 - 2024 pmbk.ru. Генерация страницы: 0.001
При копировании материалов приветствуются ссылки.
Яндекс.Метрика