Is there a way to late-initialize a member variable (a class) in C++?

荒凉一梦 提交于 2019-12-08 16:39:24

问题


I am coming from the Java background. I have the following program.

#include <string>
#include <iostream>

class First {
    public:
    First(int someVal): a(someVal) {

    }
    int a;
};

class Second {
    public:
    First first;
    Second()  {   // The other option would be to add default value as ": first(0)"
        first = First(123);

    }
};

int main()
{
    Second second;
    std::cout << "hello" << second.first.a << std::endl;
}

In class Second, I wanted to variable first to remain uninitialized until I specifically initialize it in Second()'s constructor. Is there a way to do it? Or am I just left with 2 options?:

  1. Provide a parameter-less constructor.
  2. Initialize it with some default value and later re-assign the required value.

I can't initialize first in the initializer-list with the right value, since the value is obtained after some operation. So, the actual required value for first is available in Second() constructor only.


回答1:


MY suggestion: Use a function:

private: static int calculate_first(int input) {return input*5;}
explicit Second(int input) : first(calculate_first(input)) {}

Base classes will be initialized in the order they're declared in the class inheritance list, and then members will be initialized in the order that they're listed in the class, so the calculation can depend on non-static member-variables and base classes if they have already been initialized.


Alternatively:

Default constructor, then reassign:

explicit Second(int input) { first = input*5; }

Dummy value, then reassign:

explicit Second(int input) : first(0) { first = input*5; }

Use boost::optional (or std::optional as of C++17):

boost::optional<First> first;
explicit Second(int input) { first = input*5; }

Use the heap:

std::unique_ptr<First> first;
explicit Second(int input) { first.reset(new First(input*5));}
Second(const Second& r) first(new First(*(r->first))) {}
Second& operator=(const Second& r) {first.reset(new First(*(r->first)));}

Placement new:

This is tricky and not suggested 
and worse in every way than boost::optional
So sample deliberately missing.
But it is an option.



回答2:


Initialize first in the member initializer list.

It may help to perform your calculations in a helper function and use a forwarding constructor:

class Second {
public:
    Second() : Second(helper_function()) {}

private:
    Second(int calc): first(calc) {}
    static int helper_function() { return ...; }

    First first;
};



回答3:


You can just do what you said in the comments, or, you can make first a pointer to First and give it memory whenever you like, although i don't recommend this way




回答4:


One way to separate object lifetimes is to use the heap, make first a pointer and initialize it anytime you like:

class Second {
public:
    First* first;
    Second()  { 
        first = new First(123);

    }
};

of course, you'll probably want to use a smart pointer of some sort rather than a raw pointer.




回答5:


This sentence is the core of the problem:

I can't initialize first in the initializer-list with the right value, since the value is obtained after some operation.

You should know that what you want to do here is not perfect programming style in Java, either. Leaving the field with some default value and then assigning it a bit later after some calculations have been done effectively prevents it from being final, and consequently the class from being immutable.

In any case, your goal must be to push those calculations directly into the initialization of the member, using private helper functions (which may be static):

class Second {
private:
    First first;

    static int getInitializationData()
    {
        // complicated calculations go here...
        return result_of_calculations;
    }
public:
    Second() : first(getInitializationData()) {}
};

In my opinion, everything else is just a workaround and will complicate your life in the long run.




回答6:


If you don't code to explicitly initialize a member variable, the default initializer is used to initialize it.

The draft C++ standard has the following about initialization of base classes and member variables:

12.6 Initialization [class.init]

1 When no initializer is specified for an object of (possibly cv-qualified) class type (or array thereof), or the initializer has the form (), the object is initialized as specified in 8.5.

And

12.6.1 Explicit initialization [class.expl.init]

1 An object of class type can be initialized with a parenthesized expression-list, where the expression-list is construed as an argument list for a constructor that is called to initialize the object. Alternatively, a single assignment-expression can be specified as an initializer using the = form of initialization. Either direct-initialization semantics or copy-initialization semantics apply; see 8.5.



来源:https://stackoverflow.com/questions/24702484/is-there-a-way-to-late-initialize-a-member-variable-a-class-in-c

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!