简体   繁体   中英

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

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.

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(0) { first = input*5; }

Dummy value, then reassign:

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

Use boost::optional:

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

Use the heap:

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

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. 

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

你可以在评论中做你所说的,或者,你可以先做一个指向First的指针并随时给它内存,虽然我不推荐这种方式

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.

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.

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.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM