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

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

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


std::tr1::shared_ptrInvestment pInv(createInvestment); // `ec "id`a^a`e"e`a 13


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


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

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


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


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


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

Необходимо как-то преобразовать объект 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_ptrInvestment // имеем tr1::shared_ptr

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

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

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

...

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

// управления ресурсом

bool taxable2 = !((*pi2).isTaxFree); // доступ к ресурсу

// через оператор *

...


Поскольку иногда необходимо получать доступ к ресурсу, управляемому RAII-объектом, то некоторые реализации RAII предоставляют функции для неявного преобразования. Например, рассмотрим следующий класс для работы со шрифтами, инкапсулирующий «родной» интерфейс, написанный на C:


FontHandle getFont; // из С API – параметры пропущены

// для простоты

void releaseFont(FontHandle fh); // из того же API

class Font { // класс RAII

public:

explicit Font(FontHandle fh) // захватить ресурс:

:f(fh) // применяется передача по значению,

{} // потому что того требует C API

~Font {releaseFont(f);} // освободить ресурс

private:

FontHandle f; // управляемый ресурс – шрифт

};


Предполагается, что есть обширный программный интерфейс, написанный на C, работающий исключительно в терминах FontHandle. Поэтому часто приходится преобразовывать объекты из типа Font в FontHandle. Класс Font может предоставить функцию явного преобразования, например get:


class Font {

public:

...

FontHandle get const {return f;} // функция явного преобразования

...

};


К сожалению, пользователю придется вызывать get всякий раз при взаимодействии с API:


void changeFontSize(FontHandle f, int newSize); // из C API

Font f(getFont);

int newFontSize;

...

Перейти на страницу:
Нет соединения с сервером, попробуйте зайти чуть позже