Д. Вандевурд, Н.М. Джосаттис - Шаблоны C++. Справочник разработчика (2003) (1160769), страница 73
Текст из файла (страница 73)
Вместо того чтобы указывать способ выделения памяти для счетчика заранее, можно указать место хранения счетчика в качестве параметра шаблона. Этот параметр и есть стратегией счетчика (см. главу 15, "Классы свойств и стратегий"). Интерфейс этой стратегии может состоять из функции, возвращающей значение целого типа, и функции, выделяющей при необходимости память для этого целого. Однако по ряду причин требуется обеспечить интерфейс несколько более высокого уровня.
20.2.2. Параллельный доступ к счетчику В среде с единственным потоком выполнения управление счетчиком тривиально. Базовыми операциями являются увеличение, уменьшение и сравнение значения счетчика с нулем. Однако во многопоточной среде счетчик может совместно использоваться интеллектуальными указателями из разных потоков выполнения.
В этом случае необходимо добавить интеллектуальный указатель к самому счетчику ссылок, с тем, например, чтобы иметь возможность упорядочить операции увеличения значения счетчика со стороны разных потоков. На практике для этого требуется та или иная (явная или неявная) блокировка. Вмесю укиания того, каким образом осуществляется данная блокировка, можно указать интерфейс счетчика достаточно высокого уровня для введения операций блокирования. Потребуем, чтобы счетчик представлял собой класс с приведенным ниже интерфейсом. с1авз СоипсегРо11су ( ри)э11с: // Следующие специальные члены (конструкторы, // деструктор и копирующее присвоение) в ряде // случаев не обязаны быть объявлены явно, но // должны быть доступны Соипеегро11су(); СоипеегРо11су(СоипеегРо11су сопвсй); -Соиле егро11су ( ); Соипгегро11суй орегагог= (Соипгегро11су сопзгй) // Считаем, что Т вЂ” тип указываемого объекта чо1Й Бпйг(т*); // инициализация единичным значением, // возможно, с выделением памяти уо1д бйврозе(Т*); // Возможно, приводит к освобождению // счетчика чойй йпсгещепс(Т*);// Атомарное увеличение на 1 чойб с)есгетепе(Т*);// Атомарное уменьшение на 1 403 20.2.
Счетчики ссылок Ьоо1 Тв лего(т*); // проверка на равенство 0 Тнп Т, используемый в данном шаблоне, передается как параметр шаблона. Зтот тип используется только стратегиями, работающими со счетчиками. Блокировка предотвращает одновременное обращение только к счетчику, но не к объекту СоцпсйпдРСг. Следовательно, если несколько интеллектуальных указателей на некоторый объект совместно используются разными потоками, приложению может потребоваться ввести дополнительные блокировки для корректного упорядочения операций с Соипкйпдргг.
Сами интеллектуальные ушзатели не могут отвечать за блокировку на атом уровне. 20.2.3. Деструкция и освобождение памяти Когда больше нет указателей на данный объект, наша стратегия должна удалить его. В С++ это достигается с помощью оператора с)е1есе, однако зто не всегда так. Иногда выделенную для объекта память необходимо освободить с помощью другой функции, например бгее ( ) .
Кроме того„если объект на самом деле представляет собой массив, то для освобождения памяти может потребоваться использование оператора с)е1есе [) . Поскольку, как вы смогли убедиться, могут быть самые разные варианты удаления объекта н освобождения памяти, имеет смысл ввести отдельную стратегию объекша. Ее интерфейс очень прост. с1авв ОЬ3есеро11су ( рцЬ11с: // Следующие специальные члены (конструкторы, // деструктор и копирующее присвоение) в ряде // случаев не обязаны быть объявлены явно, но // должны быть доступны ОЬЭесСРо11су(); ОЬ3есеро11су(ОЬЗесвро11су сопвсй); -ОЬ3есСРо11су(); ОЬ3ессро11суй орегасок=(ОЬ3ессРо11су сопвсй); // Считаем, что Т вЂ” тип указываемого объекта чо16 Й1врове(Т*); Вполне можно обогатить эту стратегию другими операциями, которые могут использовать указываемый обьекг, например орекасог* или орегасог->.
Один из распространенных вариантов — включение дополнительной проверки при разыменовании интеллектуального указателя, проверяющей, действительно ли существует указываемый объект. С другой стороны, вполне возможно использование дополнительных параметров стратегии для подобных проверок. для краткости расширения стратегии такого рода не рассматриваются, но если вы разберетесь с принципами работы стратегии, то не составит большого труда лобавнть к ней все, что вы пожелаете. Глава 20.
Интеллектуальные укязатеди Для большинства объектов, работа с которыми осуществляется с помошью СоппбЯпдрсг, можно воспользоваться приведенной ниже простейшей стратегией объекта. // ройпгегв/вес)оЪзро11су.Ьрр с1авв Якапбагс]ОЬЭесгРо11су ( райс: Гещр1асе«куренное Т> чойс) с)1врове(Т* оЬЭесс) ( с)е1еге оЬЭесс; Понятно, что эта стратегия не годится для работы с оператором пеы [] . Замена стратегии для этого случая тривиальна // ройпгегв/вес)аггауро11су.Ьрр с1авв Яеапг)агИтгауро11су ( рпЫзс: сещр1асе«сурепате Т> чойс) с)1врове(Т* аггау) ( с)е1еге[] аггау; ) )( Заметим, что в обоих случаях реализация с)1врове() представляет собой шаблон члена. Вместо этого можно также воспользоваться параметризованным классом стратегии. Обсуждение данной альтернативы можно найти в разделе 15.1.б, стр.
287. 20.2.4. Шаблон СоннйндР~г Теперь, когда вы разобрались с интерфейсами стратегий, можно приступить к реализации интерфейса Соипг1пдргг. // ройпгегв/соппсйпдргг.Ьрр Сетр1аке«сурепате Т, сурепаще Соипеегро11су = Яйтр1енейегепсеСоилс, сурепаще ОЬэессро11су = ягапс)агс)ОЬэесгро11су> с1авв Соипсйпдркг : ргйчаее Соипсегро11су, ргйчаке ОЬЭессро11су ( рг1часе: // Сокращения: сурес)ей Соппгегро1зсу СР; Гурес)ей ОЬЭессро11су ОР; т* оЬЭесс ройпсес) со; // Указываемый объект // (МЛ ) при его отсутствии) райс: // конструктор по умолчанию (нет явной иниииаливации) 20.2. Счетчики ссылок 405 Соипт1пдРтг() тЫв->оЬ5ест ро1птес) то = )чПТП; ) // Конструктор преобразования типа // (из встроенного указателя) ехр11с1т Соипт1пдртт(Т* р) тЬ1в->1п1т(р); // Инициализация обычным указателем ) // Конструктор копирования Соипт1пдРтг (Соипт1пдРсг<Т,СР,ОР> сопвтй ср) СР((СР сопвта)ср), // Копирование стратегий ОР((ОР сопвса)ср) ( тЫв->атгасЬ(ср); // Копирование указателя // и увеличение счетчика // )(еструктор -Соипт1пдртг() ( гЫв->детасЬ(); // Уменьшение счетчика (и // уничтожение счетчика, если // зто последний его владелец) // Присвоение встроенного указателя Соипт1пдРтг<Т,СР,ОР>а орегатог> (Т* р) ( // Проверка на указание на хранимый объект аввегс(р )= тЫв->оЬ5есс ро1псед то); сЫв->с)етасЬ(); // Уменьшение счетчика (и // уничтожение счетчика, если // это последний его владелец) // Инициализация обычным указателем тЬ1в->1п1т(р) гетигп *тЫв; ) // Копирующее присвоение (с зашитой от // присвоения самому себе) Соипс1пдртг<Т,СР,ОР>а орегатог= (Соипт1пдРтг<Т,СР,ОР> сопвта ср) ( 1т (сЫв->оЬЗесс ро1псеб то 1= ср.оЬ1ест ро1псес) то) ( тЫв->е)есасЬ()) // Уменьшение счетчика (и // уничтожение счетчика, если // это последний его владелец) // Присвоение стратегий СР::орегатог=((СР сопвта)ср); ОР::орегагог=((ОР сопвта)ср); // Копирование указателя и увеличение счетчика еЫв->атеасЬ(ср); Глава 20.
Интеллектуальные указатели ) геецгп *ГЬТв; ) // Операторы, делающие данный класс // интеллектуальным указателем Т* орегаеог-> () сопят ( гегцгп гЫв->оЬЗесг рофпгей ео/ Тй орегасог* () сопяТ ( гегцгп *еЫв->оЬзесе ро1пгей го; ) // Прочие интерфейсы могут быть добавлены позже //... ргфчаее: // Вспомогательные функции // — Инициализация обычным указателем чойй 1пТГ (Т* р) ТК (р .= ИтЛ,Ь) ( СоцптегРо11су::Тпфс(р)' ) гЬТв->оЬзесг рофпгей ео = р; ) // — Копирование указателя и увеличение счетчика чойй агеасЬ(Соцпг1пдрег<Т,СР,ОР> союзка ср) ( тЬТз->оЬзесе ройпсей Го = ср.оЬ~ест рофпгей ео; Тй (ср.оЬзесс ройпкей Ео (= В)(Л В) ( соцпгегРо11су::Тпсгешепе(ср.оЪэесг ройптей ео) ) ) // — Уменьшение счетчика (и его уничтожение, если // это последний его владелец) чо1й йегас)т() ( Н (ГМв->оЬзест рофпсей ео != Ы01Ь) ( Соцпгегро11су:: йесгещепт (ГМв->оЬЭесс ройпгей Го); Н (Соцптегро11су::Тв аего(еЬТв->оЬзесг ройпгей ео))( // Уничтожение счетчика СоцпгегРо11су::йфврове(ГЬТв->оЬЗест ройптей ео); // Использование стратегии объекта 'для удаления // Указываемого объекта ОЬЭесгро11су::ййзрове(ЕЬхя->оЬ2ест ройптеб Го); 407 20.2.