Carneiro
Carneiro

Reputation: 3509

How to define a constructor with a member initializer list where initialization is very complicated

Suppose I want to have a constructor that receives some parameters, and with these parameters I can calculate the values for it's member variables. Except that the values for the member variables are not simple assignments from the parameters. They require creation of other objects and transformation of the values before they can be used as values for the member variables.

This is way to much to cram into an initializer list. Also very inefficient since you can't create variables and reuse them so you will have to copy code (and make several copies of the same object) to fit all the code in the initializer list.

The other option is not to use the initializer list and let the default constructor be called then you overwrite the values inside the constructor with neat calculations.

Now what if the class does not have a default constructor? How can one do this neatly?

/* a class without a default constructor */
struct A {
    B x1
    B x2
    A(B x1_, B x2_) : x1{x1_}, x2{x2_} {}
};

struct C {
    A a;
    C(D d) : a{/* very complicated */, /* very complicated */} {}
};

Ultimately, I just want to initialize a with two B objects but unfortunately, they require a lot of work to initialize including instantiating other objects and using tons of methods.

Upvotes: 9

Views: 5745

Answers (4)

Jan Schultke
Jan Schultke

Reputation: 39444

C++ expressions are surprisingly powerful, and you may be able to solve the problem with conditional operators etc. However, even with such tools, you eventually hit a limit where you need multiple statmeents. In such a case, can use an IILE or simply a private member function.

Immediately invoked lambda expression (IILE)

This C++11 solution does not require you to define any new symbols, so it's arguably the best and most minimal approach. However, some people may not like it due to readability.

C(D d) : a{
    [&] { /* very complicated */ }(),
    [&] { /* very complicated */ }()
  }
{} // empty constructor body

A lambda expression can contain as much complicated logic as you want. Note that the () at the end invokes it.

Private member function

Alternatively, you can define a (static) member function which contains all the complicated logic.

private:
  static B initFirst(D& d);
  static B initSecond(D& d);
public:
  A(D d) : a{initFirst(d), initSecond(d)} {}

It is also possible to call non-static member functions in the member initializer list, however, this is very dangerous because not every subobject has been initialized yet. You would have to be very careful not to call a member function in that context which depends on not-yet-initialized members.

Upvotes: 0

Stanislav Tsepa
Stanislav Tsepa

Reputation: 720

I would suggest another more clear solution, create static method in class A with all complex logic of constructing.

class A {
public:
   B x1
   B x2
   A(B x1_, B x2_) : x1{x1_}, x2{x2_} {};

   static A FromD(D d) {
       B b1, b2;
       /* Some complex logic filling b1 and b2 */
       return A(b1, b2);
   }
};

class C {
public:
    A a;
    C(D d) :
      a(A::FromD(d))
    {}
};

Note that this solution uses implicitly defined move constructor, so don't forget to revise your situation and check if you need to define it explicitly according to rule of five

Upvotes: 0

galdin
galdin

Reputation: 14034

I would use pointers in this case, Here's the modified version of your example:

//Class A is not modified
/* a class without a default constructor */
class A {
  public:
    B x1
    B x2
    A(B x1_, B x2_) : x1{x1_}, x2{x2_} {};
};



/* a class that contains an A object and needs to initialize it based on some complex logic */
class C {
  public:
    A* a;   // I declare this as a pointer
    C(D d)
      {
          // Perform all the work and create b1,b2
          a = new A(b1, b2);
      }

    ~C()    // Create a destructor for clean-up
    {
          delete a;
    }

};

Using the new operator I can initialize the object whenever I want. And since the object is in the class scope, I delete it in the destructor (at the end of the class scope)

Upvotes: 2

leewz
leewz

Reputation: 3346

How about adding some static transformation methods?

class C {
  private:
    static B transform1(D&);
    static B transform2(D&);
  public:
    A a;
    C(D d) :
      a{transform1(d),transform2(d)}
      {}
};

Related:

Upvotes: 6

Related Questions