简体   繁体   中英

c++ Using class member as a parameter in constructor

I have a class where I need to use one of the members as a constructor parameter to initialize another const member of the same class.

class A
{
private:
   M1Type m1;
   const M2Type m2;
public:
   A(x) : m1(x), m2(m1){}
};

is this a correct way to initialize m2? m1's construction is complete in the list initialization?

Yes, " m1's construction is complete " when it's used to initialize m2 and if an M2Type can be constructed from an M1Type , this is fine - but x needs a type in A(x) .

The order of initialization is the order in which you've defined the member variables in the class, not the order in which you use them in the member initializer list:

M1Type m1;
const M2Type m2;

A(M1Type x) : m2(m1), m1(x) {} // wrong order, but still ok

If the type of x can be used to construct both an M1Type and an M2Type and get the same result as if you construct an M2Type with an M1Type constructed from x , you might as well use x to construct both to not risk problems if you reorganize the member variables later on. That's not always possible though.

Example:

struct M1Type {
    explicit M1Type(double) {};
};

struct M2Type {
    explicit M2Type(M1Type) {}
};

class A {
public:
   M1Type m1;
   const M2Type m2;
public:
   // error: no matching function for call to 'M2Type::M2Type(double&)':
   // A(double x) : m1(x), m2(x) {}

   A(double x) : m1(x), m2(m1) {}   // OK
};

Members are initialized in the order they appear in the class definition (irrespective of the order in the initializer list). Hence, first m1 is initialized and you can use its value to initialize m2 .

This code

class A
{
private:
   M1Type m1;
   const M2Type m2;
public:
   A(int x) : m1(x), m2(m1){}
};

Is fine (apart from missing type of x ). Though, if possible I would rather write it like this:

class A
{
private:
   M1Type m1;
   const M2Type m2;
public:
   A(int x) : m1(x), m2(x){}
};

Because now correctness does not depend on the order of the members. It might change when refactoring. Compilers typically warn when the order in the initializer list is different, but instead of relying on a warning you can rely on code that is correct no matter what is the order of the members.


Note that compilers do not necessarily warn about this code (order of members swapped):

class A
{
private:
   const M2Type m2;
   M1Type m1;

public:
   A(int x) : m2(m1),m1(x) {}  // !! most likely UB !!
};

but it has undefined behavior if M2Type s constructor uses the value of m1 , because m1 is used uninitialized. Hence, one should be careful when initialization of members depend on each other.

Assuming M2Type has constructor accepting M1Type argument that should work.

PS: A(x) - type of x should be specified.

executing code below you can make sure it works:

#include <iostream>
class M1Type
{
public:
    int iField;
    M1Type(int x): iField{x}
    {
        std::cout << "M1Type(int x)" << iField << std::endl;
    }
};
class M2Type
{
public:
    int iField;
    M2Type(M1Type m1) : iField{ m1.iField + 500 }
    {
        std::cout << "M2Type(M1Type m1)" << iField << std::endl;
    }
};
class A
{
private:
    M1Type m1;
    const M2Type m2;
public:
    A(int x) : m1(x), m2(m1) 
    {
        std::cout << "A(int x): M1Type: " << m1.iField << "; M2Type: " << m2.iField << std::endl;
    }
};
int main()
{
    A objA(73);
}

Console output would be:

M1Type(int x)73
M2Type(M1Type m1)573
A(int x): M1Type: 73; M2Type: 573

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