Fix warning: 'Foo::fooObj1' should be initialized in the member initialization list [-Weffc++]

913 views Asked by At

foo.h

#ifndef FOO_H
#define FOO_H

class Foo
{
    int fooObj1;
    bool fooObj2;
public:
    Foo(int input1);
};

#endif

foo.cpp

#include "foo.h"

Foo::Foo(int input1)
{
    fooObj1 = input1;
    // some code logic to decide the value of fooObj2 (an example)
    // so I can't really do member initialization list.
    fooObj2 = (fooObj1 % 2 == 0);
}

So I was following a tutorial and they told me to turn on [-Weffc++] and treat warnings as errors. But when I do that, [-Weffc++] give a warning: 'Foo::fooObj1' should be initialized in the member initialization list [-Weffc++] and 'Foo::fooObj2' should be initialized in the member initialization list [-Weffc++]. But I can't really do member initialization list in this project. So how can I reslove this warning?

4

There are 4 answers

0
Lingxi On BEST ANSWER

Two solutions to get rid of the warning.

Solution 1

Make some static method, say CalculateFooObj2InitialValue, and use it in member initialization list.

Foo::Foo(int input1):
  fooObj1(input1),
  fooObj2(CalculateFooObj2InitialValue(input1)) {
  ...  
}

Solution 2

Initialize fooObj2 with a default, yet not quite meaningful, value in member initialization list. And then calculate a meaningful initial value later and assign.

Foo::Foo(int input1):
  fooObj1(input1),
  fooObj2{} {
  // some code logic to decide the value of fooObj2 (an example)
  // so I can't really do member initialization list.
  fooObj2 = (fooObj1 % 2 == 0);
}
0
Swift - Friday Pie On

-Weffc++ generates some paranoid warnings but there is an ideal you want to follow. E.g. you always can do partial list:

Foo::Foo(int input1) : fooObj1(input1) /*, fooObj2() still can be here */
{
    // some code logic to decide the value of fooObj2 (an example)
    // so I can't really do member initialization list.
    fooObj2 = (fooObj1 % 2 == 0);
}

Note,that code in body of constructor is not initialization. It's an assignment. Members of class are still initialized in order of their declaration with implementation-defined or indeterminate values.

In some multi-threaded cases the difference might be important if Foo's members are atomics and something may access them meanwhile.

It's important to analyze logic, does it require branching, is it actually a compile-time known constant? Can you use simple ternary for initialization?

In complex cases fooObj2 maybe even an object of another class-type with own ctor hiding that "complex logic", to avoid problems with determining initialization order.

0
wreathbro On
    
Foo::Foo(int input1) : fooObj1(input1), fooObj2(0 == input1 % 2) {}
0
TimVdG On
Foo::Foo(int input1)
   : fooObj1(input1)
   , fooObj2(input1 % 2 == 0)
{
}

Note that the value of fooObj2 is calculated based on the input1 parameter and not the fooObj1 value. The reasoning is that if you would change the order in your header:

class Foo
{
    bool fooObj2;
    int fooObj1;
public:
    Foo(int input1);
};

You would end up in uninitialized memory of fooObj1, if you calculate fooObj2 from fooObj1. Members are initialized in the order they appear in the class, not in the order they appear in the initialization list of the constructor.