RAII如何应用于需要扩展初始化的类成员?

时间:2011-11-21 17:26:10

标签: c++ initialization raii

据我了解RAII习惯用法应用于类所需的资源(如果我错了请纠正我),需要资源的类应定义相应类型的成员,其析构函数将在使用类实例被销毁时自动调用,如下所示:

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 个答案:

答案 0 :(得分:10)

将初始化代码包装到函数中,并使用该函数(成员或非成员,静态或非静态,视情况而定)初始化成员变量:

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

答案 1 :(得分:2)

也许您应该将纹理的创建封装到自由函数中,因为文件的读取似乎与Skybox无关,并且可能在其他地方有用。我想其他名称是Factory

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

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

更好的是来自Tex的Skybox可构造 宾语。但是,这需要复制或移动Tex 构造的。如果不是这种情况,可以采取适当的解决方法 返回std::unique_ptr<Tex>

答案 2 :(得分:0)

使用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等函数的实现。

答案 3 :(得分:0)

如果Texture类具有默认构造函数并支持交换,则可以使用局部变量初始化资源,并在构造函数的末尾交换它。

Skybox::Skybox(const std::string& fileName)
{
    Texture localTex(fileName);
    //...
    tex_.swap(localTex);
}