#Acceptable to default initialise and then re-assign in constructor?

14 messages · Page 1 of 1 (latest)

stoic hound
#

Given some code such as

class Foo {
  Bar b1;
  Car c1;

  Foo(const InitInfo& info) {
    this->b1 = generateBar(info);
    this->c1 = generateCar(info);
  }
};

I cannot know the exact objects that can be assigned to b1 and c1 until I am in the constructor. There are two possible ways I can see to deal with this.

Firstly, default initialise b1 and c1 in the declaration (assuming they have constructors that allow for this) and then re-assign to them in the constructor.

Secondly, wrap the declarations of b1 and c1 in unique_ptrs so they get default initialised to nullptr and then assign the correct object to them in the constructor so they only get assigned an object to them once rather than twice.

The downside I can see the the first option is that if there is a destructor that relies on the contents of the object of Bar or Car then the destructor will be called twice, with one of those calls being for a default initialised object which may not have the correct data and could cause an error. Additionally, if the object doesn't have a default constructor then you will be stuck.

Which way would you recommend? If you have an alternative, what is that and how would that way be better than these two options given?

outer blazeBOT
#

When your question is answered use !solved to mark the question as resolved.

Remember to ask specific questions, provide necessary details, and reduce your question to its simplest form. For more information use !howto ask.

snow zodiac
#

I see another way

#

generate bar and car outside first, then you only need to pass them as parameters

vivid valley
#

Use member initializer lists?

small needle
#

I think the main issue is that he needs to reach the constructor's body before knowing the exact desired initial value

#

at least that's how I interpret

I cannot know the exact objects that can be assigned to b1 and c1 until I am in the constructor

vivid valley
small needle
#

yeah, the example isn't very illustrative of that

#

though maybe generateCar is actually a member function that depends on the proper initialization of some members or whatever... not having sufficient information when within the member initializer list is kinda niche but not impossible

#

The downside I can see the the first option is that if there is a destructor that relies on the contents of the object of Bar or Car then the destructor will be called twice, with one of those calls being for a default initialised object which may not have the correct data and could cause an error.
this bit makes no sense to me though
the first option was a priori about (default) initializing then assigning the correct value from the body
the first question here would be: what destructor are you talking about?

  • if you mean the destructor of Foo, it will be called once when whatever object you're constructing is going to be destroyed
  • if you mean the destructor of Bar, and specifically about the member b1, then it will also be destructed only once; likewise for c1
  • if you mean you create a temporary Bar to assign it to b1, and that temporary is going to be destroyed... then sure, there is another destructor call, but it's also on another object, so there is no double destruction occurring here
#

if you meant something else, then what did you mean?

#

If you have an alternative, what is that and how would that way be better than these two options given?
bad_doggo already mentioned constructing the thing beforehand, which may or may not be an option
if we stick to the "fill in the value later" approach:

  • you've already mentioned constructing the objects then changing them (which can be achieved a number of different ways)
  • you've also already mentioned using dynamic allocation/memory and pointers
  • you're missing at least one option, which is to use std::optional to delay creation
#

well I guess even that has some variants about how you'd set it up, but the big difference compared to using pointers/dynamic allocation is that the optional object is actually part of Foo, instead of Foo keeping track of an dynamic object via a pointer