How to apply RAII for class members requiring extended initialization?

As far as I understand the RAII idiom when applied to the resources needed by the class (and please correct me if I am mistaken), the class requiring the resource must determine the member of the corresponding type, and its destructor will be called automatically when the instance of the used class is destroyed, for example:

class Skybox { public: Skybox() : tex_(...) {} private: Texture tex_; }; 

Besides using a smart pointer to allocate a resource on the heap, how can this template be applied if a resource member requires some code to be executed in the Skybox constructor before the resource is initialized? For instance:

 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 } 

Update: the Texture class requires that all initialization be performed in its constructor (i.e. the Texture::Init() method is available)

+4
source share
4 answers

Insert the initialization code into the function and use this function (member or non-member, static or non-static, if necessary) to initialize the member variable:

 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)) { } 

The initialization function should probably be a static function. If this is not the case, be careful not to use any elements that have not yet been initialized - you call init_tex on the not yet fully initialized Skybox instance.

+10
source

Perhaps you should encapsulate the creation of a texture in a free function, as reading the file seems unrelated to Skybox and may be useful elsewhere. I guess the other name for this is Factory .

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

Even nicer will be the Skybox, which can be built using the Tex object. However, this requires Tex copy or move constructively. If this is not the case, a proper workaround might be to return a std::unique_ptr<Tex> .

+2
source

Using the capabilities of C ++ 11 (variable templates and perfect redirection), this can be achieved thanks to the template constructor:

 #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; } 

In C ++ 03/98, the template constructor is still a solution (but boost should help for a variational template and passing arguments). See implementations of functions such as make_share_ptr.

0
source

If the Texture class has a default constructor and supports swapping, you can initialize the resource of the local variable and change it at the end of the constructor.

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

Source: https://habr.com/ru/post/1382328/


All Articles