Программирование >>  Оптимизация возвращаемого значения 

1 ... 52 53 54 [ 55 ] 56 57 58 ... 96


Легко было бы добавить к имеющимся классам интеллектуальных указателей функцию-член 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;



1 ... 52 53 54 [ 55 ] 56 57 58 ... 96

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