Анимация
JavaScript


Главная  Библионтека 

0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 [ 70 ] 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85

private:

Т* object; Func f;

Теперь любой, кто захочет, может иметь list<callbackbase*> и полиморфно вызывать оператор operatorO элементов этого списка. Конечно, использование 1 i st<shared ptr<cal 1 back> > еще предпочтительнее; см. [Sutter02b].

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

> Рекомендация

Рассмотрите возможность использования полиморфизма, с тем чтобы различные инстанцирования вашего шаблона класса могли использоваться взаимозаменяемо (если это имеет смысл для вашего шаблона класса). Если это так, то можно легко добиться полиморфизма, обеспечив наличие базового класса, совместно используемого всеми инстанцированиями шаблона класса.

10. (Идиома, компромисс). Можно обеспечить вспомогательную функцию таке саПЬаск для облегчения вывода типов. Сейчас пользователь должен явно указывать аргументы шаблона для временных объектов.

1ist< cal1back< widget > > 1;

1.push back( callback<widget>( w, Awidget::someFunc ) );

Ho зачем писать widget дважды? Неужели компилятору это и так непонятно? Да, непонятно, но вы можете помочь ему в контексте, когда нужны только временные объекты наподобие рассматриваемого. Вы можете разработать вспомогательную функцию.

list< callback< widget > > 1;

l.push back( make cal1 back ( w, &widget::SomeFunc ) );

Эта функция makecal 1 back работает так же, как стандартная std: :make pai г. Это должна быть шаблонная функция, поскольку компилятор выводит типы только для этого вида шаблонов. Вот как должна выглядеть эта функция.

tempiate<typename т >

callback<T> make cal1back( т& t, void (T::*f) () ) { return callback<T>( t, f );

11. (Компромисс). Добавление поддержки других сигнатур обратного вызова. Больше всего работы я оставил напоследок. Перефразируя, можно сказать: "Есть много, друг Горацио, на свете сигнатур, которые и не снились нашей void (т::*F) ()!"

> Рекомендация

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

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



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

Я не хочу приводить здесь весь исходный текст, так как он достаточно скучный. (Есяи вы действительно хотите познакомиться с этим утомительно повторяющимся кодом, или если у вас бессонница - возьмите книгу [AlexandrescuOl], там вы найдете подобные примеры.) Я же только вкратце приведу несколько замечаний по этому поводу.

Во-первых, следует подумать о константных функциях-членах. Простейший способ работы с ними - обеспечить параллельный обратный вызов, который использует соответствующую сопяг-сигиатуру, и в эгой версии не забывать получать и хранить т по ссылке или указателю на const.

Во-вторых, надо вспомнить о различных возвращаемых типах. Простейший способ обеспечить варьируемый юзвращаемый тип - это добавить еще один параметр шаблона.

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

Увы, рост кода приобретает просто взрывной характер, и вам надо подумать и вовремя остановиться, искусственно ограничив количество поддерживаемых параметров функций обратного вызова. Возможно, в будущем стандарте С++Ох мы и найдем что-то наподобие возможности работы с шаблонами с переменным числом параметров, но пока что это - пустые мечты.

Резюме

Обобщая все сказанное и добавив микроулучшения наподобие последовательного использования ключевого слова typename и соглашений об именовании, мы получим окончательную версию кода.

class callbackBase { public:

virtual void operatorO () const { }; vi rtual -callbackBaseC) = 0;

CallbackBase::-CallbackBaseC) { }

tempiate<typename T>

class Callback : public CallbackBase {

public:

typedef void (t::*F)();

CallbackC T& t, F f ) : t„(&t), f (f) { } void operatorO () const { (t ->*f )0 ; }

private: T* t ; F f ;

tempiate<typename т>

callback<T> make callback( т& t. void (T::*f) () ) { return callback<t>( t, f );



Задача 36. Объединения Сложность: 4

Речь в данной задаче пойдет не о профсоюзах или партиях, а об объединениях в С++, их сильных и слабых сторонах и о правилах использования конструируемых объектов в качестве членов объединений.

Вопрос для новичка

1. Что такое объединения и для чего они предназначены?

2. Какие типы не могут использоваться в качестве членов объединений? Почему существуют такие ограничения? Поясните свой ответ.

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

3. в статье [Мап1еу02] рассматривается написание языка сценариев, в котором используются объединения. Допустим, что вы хотите, чтобы ваш язык поддерживал единый тип для переменных, которые в разные моменты времени могут хранить целые числа, строки или списки. Создание union {int i; Tist<int> 1; string s;}; не работает no причинам, которые разбираются в вопросах 1 и 2. Приведенный далее код представляет собой обходной путь, который пытается обеспечить поддержку произволыюго типа в качестве участника объединения (более подробную информацию по этому вопросу вы сможете найти в указанной статье.)

Отрецензируйте предложенный код и найдите:

а) механические ошибки, такие как неверный синтаксис или непереносимые соглашения;

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

#include <list> #include <string> #include <iostream> usi ng namespace std;

#define max(a,b) (a)>(b)?Ca):(b)

typedef list<int> LIST; typedef string STRING;

struct MYUNION {

MYUNIONO : currtypeC none ) {} -MYUNION0 {cleanupO;}

enum uniontype {N0NE, INT, LIST, STRING}; uniontype curetype;

inline i nt& getintC); inline LIST& qetlistO; inline STRING& getstringO;

protected: union {

i nt i ;

unsigned char buff[max(sizeof(LIST),sizeof(STRING))];

} U;

void cleanupO ;



0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 [ 70 ] 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85