|
Программирование >> Оптимизация возвращаемого значения
Легко было бы добавить к имеющимся классам интеллектуальных указателей функцию-член isNull, но это не помогло бы решить проблему, которая заключается в том, что при проверке на равенство нулю интеллектуальные указатели не ведут себя как простые указатели. Другой подход - задать оператор явного преобразования, который позволяет компилироваться вышеприведенному коду проверок. Обычно при этом выполняется преобразование к типу void*: template<class Т> class SmartPtr { public: operator void*(); SmartPtr<TreeNode> ptn; if (ptn ==0) ... if (ptn) ... if {!ptn) . .. Возвращает 0, если интеллектуальный указатель равен null, иначе ненулевое значение. Теперь нормально. Также нормально. Нормально. Это аналогично преобразованию для классов входных потоков и объясняет, почему можно написать такой код: ifstream inputFile( datafile.dat ); if (inputFile) . . . Проверка успешного открытия файла inputFile. Данная функция, так же как и все функции преобразования типов, имеет один недостаток - вызовы функций завершаются успешно, хотя большинство программистов ожидает, что они завершатся неудачей (см. правило 5). В частности, такое преобразование позволяет сравнивать интеллектуальные указатели совершенно различных типов: SmartPtr<Apple> ра; SmartPtr<Orange> ро; if (ра == ро) . . . Компилируется нормально! Даже если для типов SmartPtr<Apple> и SmartPtr<Orange> не предусмотрена функция operator=, этот код будет компилироваться без ошибок, поскольку интеллектуальные указатели могут быть неявно преобразованы к указателям типа void*, а для встроенных указателей существует встроенная функция сравнения. Такое поведение делает функции неявного преобразования типов довольно опасными (см. правило 5). Это вариации на тему преобразования к void*. Некоторые разработчики поддерживают возможность преобразования к const void*, другие выбирают преобразование в тип bool. Ни одна из названных разновидностей не устраняет проблему сравнений различных типов. ptn не равен null. if (ptn == 0) ... Все еще ошибка, if (ptn) ... Также ошибка. Риск возникает только при таких сравнениях различных типов: SmartPtr<Apple> ра; SmartPtr<Orange> ро; if (!ра== !ро) ... Увы, компилируется. К счастью, программисты не часто пишут подобный код. Интересно, что в библиотеке iostream кроме неявного преобразования к void* задана функция operator!, но эти две функции обычно проверяют немного различные состояния потока. (В стандарте библиотек языка C-i-i- (см. правило 35) неявное преобразование к типу void* заменено неявным преобразованием к типу bool, а функция operator bool всегда возвращает отрицание operator!.) Преобразование интеллектуальных указателей в обычные Иногда требуется добавить интеллектуальные указатели к приложению или библиотеке, где уже используются обычные указатели. Например, распределенная база данных может изначально не быть распределенной, так что некоторые старые библиотечные функции могут не предназначаться для работы с интеллектуальными указателями: Существует промежуточное решение, позволяющее использовать приемлемую синтаксическую форму для проверки на равенство нулю, уменьшая при этом риск случайного сравнения интеллектуальных указателей разных типов. Оно состоит в перегрузке operator! в классе интеллектуального указателя, чтобы функция operator! возвращала значение true тогда и только тогда, когда интеллектуальный указатель, для которого она вызывается, равен нулю. template<class Т> class SmartPtr { public: bool operator!{) const; Возвращает true тогда и только ... тогда, когда интеллектуальный указатель равен null. Это позволяет создавать такие клиентские программы: SmartPtr<TreeNode> ptn; if {!ptn) { Нормально; указатель ptn равен null. else { } НО не такие: class Tuple {...}; Как и раньше. void normalize (Tuple *pt); Привести указатель *pt к канонической форме; используется обычный указатель. Рассмотрим, что произойдет, если вы попытаетесь вызвать функцию normalize для интеллектуального указателя на объект Tuple: DBPtr<Tuple> pt; normalize(pt); Ошибка! Этот вызов не скомпилируется, поскольку не существует способа преобразовать DBPtr<Tuple> в Tuple*. Вы можете заставить его работать, сделав следующее: normalize(&*pt); Грубо, но допустимо. НО я надеюсь, вы согласитесь со мной, что это далеко не лзший вариант. Можно сделать вызов успешным, добавив к шаблону интеллектуального указателя на Т оператор явного преобразования в обычный указатель на Т: template<class Т> Как и раньше, class DBPtr ( public: operator Т*О { return pointee; } DBPtr<Tuple> pt; normalize(pt) ; Теперь работает. Добавление этой функции также устраняет проблему проверки на равенство нулю: if (pt == 0) ... Нормально, преобразует указатель pt в Tuple*, if (pt) ... То же. if (!pt) ... То же. Тем не менее, у таких функций преобразования есть и недостатки. (Это почти всегда так - см. правило 5.) Данные функции позволяют клиентам непосредственно использовать в программе обычные указатели, пренебрегая интеллектуальностью разработанных вами объектов-указателей. void processTuple(DBPtr<Tuple>& pt) { Tuple *rawTuplePtr = pt; Преобразует DBPtr<Tuple> в Tuple* . для изменения tuple используется rawTuplePtr;
|
© 2006 - 2024 pmbk.ru. Генерация страницы: 0
При копировании материалов приветствуются ссылки. |