Как можно применять RAII для членов класса, которые требуют расширенной инициализации? - PullRequest
4 голосов
/ 21 ноября 2011

Насколько я понимаю идиому RAII применительно к ресурсам, необходимым для класса (и, пожалуйста, исправьте меня, если я ошибаюсь), класс, которому требуется ресурс, должен определить члена соответствующего типа, и его деструктор будет вызываться автоматически при уничтожении экземпляра класса using, например:

class Skybox
{
    public:
        Skybox() : tex_(...) {}

    private:
        Texture tex_;
};

Помимо использования интеллектуального указателя для выделения ресурса в куче, как можно применить этот шаблон, если член ресурса требует выполнения некоторого кода в конструкторе Skybox перед инициализацией ресурса? Например:

class Skybox
{
    public:
        Skybox(const std::string& fileName);

    private:
        Texture tex_;
}

Skybox::Skybox(const std::string& fileName)
{
    // read stuff from skybox initialization file
    // including various texture parameters such as texture file
    ...
    // initialize tex_ based on information read above
}

Обновление: класс Texture требует, чтобы вся инициализация была выполнена в его конструкторе (т. Е. Метод Texture::Init() недоступен)

Ответы [ 4 ]

10 голосов
/ 21 ноября 2011

Оберните код инициализации в функцию и используйте эту функцию (член или не член, статический или нестатический, в зависимости от ситуации) для инициализации переменной-члена:

Texture Skybox::init_tex(std::string const& fileName) {
  // read stuff from file, including textureFile
  // initialize result
  return Texture(...);
}

Skybox::Skybox(std::string const& fileName):
  tex_(init_tex(fileName))
{ }

Функция инициализации должнавероятно быть статической функцией.Если это не так, будьте осторожны, чтобы не использовать членов, которые еще не были инициализированы - вы вызываете init_tex для еще не полностью инициализированного Skybox экземпляра.

2 голосов
/ 21 ноября 2011

Возможно, вам следует заключить создание текстуры в бесплатную функцию, поскольку чтение файла кажется не связанным со скайбоксом и может быть полезным в другом месте.Я предполагаю, что другое имя для этого - Factory.

Tex tex_from_file(const std::string&) {
  // ...
}

class Skybox {
  Skybox(const std::string& s) : tex_(tex_from_file(s)) {}
};

Еще приятнее было бы создание Skybox из объекта Tex.Однако для этого требуется Tex, чтобы его можно было копировать или перемещать.Если это не так, правильным решением может быть возврат std::unique_ptr<Tex>.

0 голосов
/ 22 ноября 2011

Если класс Texture имеет конструктор по умолчанию и поддерживает подкачку, вы можете инициализировать ресурс локальной переменной и поменять его местами в конце конструктора.

Skybox::Skybox(const std::string& fileName)
{
    Texture localTex(fileName);
    //...
    tex_.swap(localTex);
}
0 голосов
/ 21 ноября 2011

Используя функции C ++ 11 (шаблоны с переменным числом аргументов и совершенную пересылку), этого можно достичь благодаря конструктору шаблонов:

#include <utility>
template<class T>
class raii_wrapper
{
    public:

template<typename... Arg>
    raii_wrapper(Arg&&... args) : obj(std::forward<Arg>(args)...) {}

    private:
        T obj;
};

struct foo
{
    foo(){}
};

struct foo_1
{
    foo_1(int){}
};

struct foo_2
{
    foo_2(int,int&){}
};

int main()
{
    raii_wrapper<foo> f;
    raii_wrapper<foo_1> f1(1);
    int i(3);
    raii_wrapper<foo_2> f2(1,i);
    return 0;
}

В C ++ 03/98 конструктор шаблона по-прежнему является решением (но boost должен помочь при передаче переменных шаблонов и аргументов). Смотрите реализации таких функций, как make_share_ptr.

...