На этом шаге мы рассмотрим особенности использования объектов auto_ptr в классах.
Использование auto_ptr в качестве переменных классов помогает предотвратить утечку ресурсов. Если заменить обычные указатели на auto_ptr, отпадет необходимость в деструкторе, поскольку объект будет автоматически удаляться при уничтожении переменной. Кроме того, auto_ptr помогает предотвратить утечку ресурсов, обусловленную исключениями во время инициализации объекта. Учтите, что деструкторы вызываются только в том случае, если конструирование было завершено. Следовательно, если исключение происходит внутри конструктора, то деструкторы будут вызваны только для полностью сконструированных объектов. Это может привести к утечке ресурсов, например, если первый вызов new завершился успешно, а второй вызвал сбой. Пример:
class ClassB { private: ClassA* ptr1; // Переменные-указатели ClassA* ptr2; public: // Конструктор, инициализирующий указатели, // Если при втором вызове new произойдет исключение. // возникнет утечка ресурсов. ClassB (ClassA val1, ClassA val2) : ptr1 (new ClassA(val1)), ptr2(new ClassA(val2)) { } // Копирующий конструктор. // Исключение при второй вызове new может привести к утечке ресурсов. ClassB (const ClassB& x) : ptr1(new ClassA(*x.ptr1)), ptr2(new ClassA(*x.ptr2)) {} // Оператор присваивания const ClassB& operator= (const ClassB& x) { *ptr1 = *x.ptr1; *ptr2 = *x.ptr2; return *this; } ~ClassB () { delete ptr1; delete ptr2;} . . . . }
Для предотвращения возможной утечки ресурсов достаточно заменить указатели объектами auto_ptr:
class ClassB { private: const std::auto_ptr<ClassA> ptr1; // Переменные auto_ptr const std::auto_ptr<ClassA> ptr2; public: // Конструктор, инициализирующий auto_ptr. // Утечка ресурсов невозможна. ClassB (ClassA val1, ClassA val2) : ptr1(new ClassA(val1)), ptr2(new ClassA(val2)) { } // Копирующий конструктор. // Утечка ресурсов невозможна. ClassB (const ClassB& x) : ptr1(new ClassA(*x.ptr1)), ptr2(new ClassA(*x.ptr2)) { } // Оператор присваивания const ClassB& operator= (const ClassB& x) { *ptr1 = *x.ptr1; *ptr2 = *x.ptr2; return *this; } // Деструктор не нужен (деструктор по умолчанию // дает возможность ptr1 и ptr2 уничтожить их объекты). . . . . }
Хотя деструктор можно опустить, все равно придется запрограммировать копирующий конструктор и оператор присваивания. По умолчанию они оба пытаются передавать право владения, чего, по всей вероятности, быть не должно. Кроме того, как упоминалось ранее, для предотвращения непредвиденной передачи прав владения следует использовать константный объект auto_ptr, если объект, на который ссылается auto_ptr, не должен изменяться на протяжении его жизненного цикла.
На следующем шаге мы рассмотрим неправильное использование класса auto_ptr.