Is this a bad RAII design?

I come from the Java background, but after that I learned C ++ and programmed it for several years (mainly debugging and recording fixes, not developing programs from scratch). However, I ran into a problem today and, frankly, I am a little surprised that it took a long time to meet her.

Let's say I have a class called Class1 whose header file contains (among other code):

class Class1 { private: Class2 object; } 

Class2 does not have a specified default constructor. Now, in the Class1 constructor, I read the binary header of the file and use the information that I parse to initialize Class2, as shown below using pseudocode:

 Class1::Class1(std::string) { // Read some binary info from a file here // Parse that binary info object2 = Class2(info); 

In Java, since it does not use the RAII paradigm, this would be completely legal. However, since C ++ uses RAII, object 2 has already been initialized using its default constructor when I do object2 = Class2(info); . I could not just name this constructor initially (in the Class1 header file) because I did not have the information I needed to create an object . However, I cannot just make object2 local to the constructor, because I need other functions to see / use it.

Clearly this does not work. What is the standard approach for this? I really thought that I just changed Class1 to a class 2 pointer:

 class Class1 { private: Class2* objectPointer; } 

and then calling *objectPointer = Class2(info) . However, "Class2" in my case is ifstream, and it seems that the operator= function has been removed and does not work with any of the approaches.

So ... how to do this?

+4
source share
4 answers

because your object not const , that everything is completely legal. However, if you want to initialize objects at the initialization stage, you must provide information. You can do it

  Class1::Class1(std::string file_name) : object(InfoFromFile(file_name)) {} 

where InfoFromFile() will be either a standalone function (declared in the anonymous namespace inside the .cc file) or a static member function of Class1 . If you need more information than file_name to generate the information needed for Class2 , you can provide it to this function.

+6
source

If the β€œread and parse” part does not require the object to be constructed, you can transfer it to a static (or non-member) function and initialize the element using the result:

 Class1::Class1(std::string) : object2(read_class2_info(some_file)) {} 

If you really cannot separate the reading of the file from the construction of the object for any reason and cannot reassign object2 later, then you will need to use a pointer. To create an object, you must use new :

 objectPointer = new Class2(info); 

However, to get rid of the need to tinker with Rule three , you should avoid managing dynamic objects yourself and use the smart pointer instead:

 std::unique_ptr<Class2> objectPointer; objectPointer.reset(new Class2(info)); 
+5
source

I suggest using a local function to execute bits that read binary information and are directly initialized in the constructor list:

 namespace { InfoObject readInfo(std::string s) { // Read some binary info from a file here // Parse that binary info return info; } } Class1::Class1(std::string s) : object(readInfo(s)) { } 

Using a pointer, of course, is also an option. This is why it works more naturally in Java (each user type is a pointer, inside). Most likely, you will want to use a smart pointer.

+2
source

I think you will need to initialize the object dummy data and then update it with new data after parsing it.

Something like that:

 Class1(std::string str) : object("some valid-but-meaningless data") { // parse info object = Class2(info); 

If this is not good, you will probably have to parse the parsing in the static factor method, which then passes the information to the constructor (which you can make private). Something like that:

 Class1(Info info) : object(info) { // whatever else you want } static Class1 create(std::string) { // parse info return Class1(info); } 

EDIT: I actually like the way Walter responds better; using a function in initialization is a good idea. I just leave it here for some alternative ideas that you might consider.

+1
source

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


All Articles