Эффективное использование C++. 55 верных способов улучшить структуру и код ваших программ | страница 62



Правило 15: Предоставляйте доступ к самим ресурсам из управляющих ими классов

Управляющие ресурсами классы заслуживают всяческих похвал. Это бастион, защищающий от утечек ресурсов, а отсутствие таких утечек – фундаментальное свойство хорошо спроектированных систем. В идеальном мире вы можете положиться на эти классы для любых взаимодействий с ресурсами, не утруждая себя доступом к ним напрямую. Но мир неидеален. Многие программные интерфейсы требуют доступа к ресурсам без посредников. Если вы не планируете отказаться от использования таких интерфейсов (что редко имеет смысл на практике), то должны как-то обойти управляющий объект и работать с самим ресурсом.

Например, в правиле 13 изложена идея применения интеллектуальных указателей вроде auto_ptr или tr1::shared_ptr для хранения результата вызова фабричной функции createInvestment:


>std::tr1::shared_ptr pInv(createInvestment()); // èç ïðàâèëà 13


Предположим, есть функция, которую вы хотите применить при работе с объектами класса Investment:


>int daysHeld(const Investment *pi); // возвращает количество дней

>// хранения инвестиций


Вы хотите вызывать ее так:


>int days = daysHeld(pInv); // ошибка!


но этот код не скомпилируется: функция daysHeld ожидает получить указатель на объект класса Investment, а вы передаете ей объект типа tr1::shared_ptr .

Необходимо как-то преобразовать объект RAII-класса (в данном случае tr1::shared_ptr) к типу управляемого им ресурса (то есть Investment*). Есть два основных способа сделать это: неявное и явное преобразование.

И tr1::shared_ptr, и auto_ptr предоставляют функцию-член get для выполнения явного преобразования, то есть возврата (копии) указателя на управляемый объект:


>int days = daysHeld(pInv.get()); // нормально, указатель, хранящийся

>// в pInv, передается daysHeld


Как почти все классы интеллектуальных указателей, tr1::shared_ptr и auto_ptr перегружают операторы разыменования указателей (operator-> и operator*), и это обеспечивает возможность неявного преобразования к типу управляемого указателя:


>class Investment { // корневой класс иерархии

>public: // типов инвестиций

>bool isTaxFree() const;

>...

>};

>Investment *createInvestment(); // фабричная функция

>std::tr1::shared_ptr // имеем tr1::shared_ptr

>pi1(createInvestment()); // для управления ресурсом

>bool taxable1 = !(pi1->isTaxFree()); // доступ к ресурсу

>// через оператор ->

>...

>std::auto_ptr pi2(createInvestment()); // имеем auto_ptr для