Лучший способ использовать RAII условно - PullRequest
3 голосов
/ 17 июня 2011

У меня есть хороший класс управления ресурсами.Для конкретности, пусть это будет класс File для управления FILE * (обработка операций открытия и закрытия)

Каков обычный подход, когда существуют случаи, когда мне не нужно управлять ресурсом, а кто-то еще несет ответственность?

В целях иллюстрации у меня сейчас есть что-то вроде этого:

int main(int argc, char** argv)
{
    File my_file(argv[1]); //I unconditionaly obtain the resource
    //...
    return 0;  //and unconditionally relinquish with the destructor
}

И я хочу что-то вроде

int main()
{
    if(argc <= 1){
        //use stdin that is already available
    }else{
        //obtain a file from argv[1]
    }
    //...
    if(argc <= 1){
        //nothing to do
    }else{
        //close the file we obtained
    }
}

(но менее уродливо), более надежный и т. д ...)

Ответы [ 4 ]

6 голосов
/ 17 июня 2011

boost::shared_ptr позволяет вам перейти в пользовательский деструктор. Если вы переносите указатель с внешним управлением, вы можете передать no-op:

namespace {
template<typename T>
void noop_destruct(T *) throw() { }
}

template<typename T>
boost::shared_ptr<T> make_dummy_shared_ptr(T *p) {
    return boost::shared_ptr<T>(p, noop_destruct);
}

Теперь, когда вам нужен настоящий объект RAII, используйте обычный boost::shared_ptr, а когда вам нужен поддельный, используйте такой адаптер - он будет действовать точно так же, как обычный указатель.

2 голосов
/ 17 июня 2011

Ваш класс RAII уже сохраняет достаточно состояния, чтобы знать, когда уничтожать ресурс, которым он управляет. Он также может содержать флаг, указывающий, должен ли ресурс быть уничтожен, или вы можете использовать специальное значение на счетчике, чтобы указать, что ресурс контролируется вне класса.

Тогда все, что вам нужно, это способ контролировать состояние при получении ресурса. Например, вы можете иметь два разных конструктора или параметр в конструкторе со значением по умолчанию. Вы можете иметь метод Attach, который присоединяет существующий ресурс. Это полностью зависит от вас.

2 голосов
/ 17 июня 2011

Вы можете использовать логику того, использовать ли ресурс внутри вашего класса управления ресурсами. Тогда это больше не обусловлено. Просто сделай

int main(int argc, char** argv)
{
    File my_file(argc > 1 ? argv[1]: NULL); //If NULL, File will point to stdin
    //...
    return 0;  //File's destructor will run, relinquishing resources if necessary.
}
0 голосов
/ 17 июня 2011

Большинство распространенных шаблонов не допускают этого. Однако вы можете разрешить использование пользовательских плагинов-распределителей, которые Стандарт имеет для своих контейнеров, которые будут учитывать эту семантику. Это краткий образец -

class Allocator {
    File* Allocate(...) {
        return fopen(...);
    }
};
class MyStdinAllocator {
    File* Allocate(...) {
        return ...;
    }
};
template<typename MyAllocator = Allocator> class File {
    File* ptr;
    Allocator alloc;
    File(..., const Allocator& allocref)
    : alloc(allocref) {
        ptr = alloc.Allocate(...);
    }
};
...