Default Member Values Best Practice

Default member values best practice

If a class member is always initialized with the same initial value, then you should make the initializer inline, so as to avoid duplication. If the initial value depends on the constructor, then put it in the constructor initializer list. (And never use assignment in the way you did.)

Example:

class Foo
{
bool done = false; // always start like this
int qty;
Bar * p;

public:
Foo() : qty(0), p(nullptr) { }
Foo(int q, Bar * bp) : qty(q), p(bp) { }
explicit Foo(char const * s) : qty(std::strlen(s)), p(new Bar(s)) { }

// ...
};

In this hypothetical example, the member done is always required to start as false, so it's best to write the initializer inline. The other two members, qty and p, can be initialized differently in each of three different constructors, so they are initialized inside the constructors' initializer lists.

A curiosum: Note that providing an inline initializer prevents your class from having a trivial default constructor.

Default initialization of member variables or add more constructors? Best practice for creating classes?

You can rewrite your constructors to not have any default parameters by adding some constructors

std::string m_color{ "black" };
double m_radius{10.}; // specify default value here, exactly like color

public:

Ball() = default; // add a default constructor

Ball(double radius) // keep the constructor taking a double
: m_radius{ radius } {}

Ball(const std::string& color) // add a constructor taking only a string
: m_color{ color } {}

Ball(const std::string& color, double radius) // don't provide defaults for the
: m_color{ color }, // 2 argument constructor
m_radius{ radius } {}

These overloads for the constructor are equivalent to your version, and the defaults are specified only once.

Is it good practice to initialize a member variable of a class in the header file?

Initializing a member in the class body is completely equivalent to doing it in the member initializer list in the construcor. (But if you provide initializers in both places, member-init-list overrides initializers in class body.)

Initializing in the class body whenever possible is a good practice, because it's less error prone (you'll immediately notice if you forget to initialize a newly added member; see DRY).

Best practice for class member initialization

Do everything in Foo constructor(s)!

Do all construction and initialisation in constructors! This is known as RAII: resource acquisition is initialisation. Allocating a resource, in this case a Foo should initialise the resource as well. This also makes your interface easy to use: Foo cannot be used without first at least constructing a Foo. Once constructed, assuming it initialises everything in its constructor, it's ready to use. If you expect callers to also have to call an Initialize or Set function, you are making your class interface hard to use. How will Foo respond to function calls if Initialize hasn't first been called? Suddenly each function would need to have the following:

bool Foo::SomeFunction()
{
if (!mBar->isInitialized())
return false;

// Do what we came here to do
return true;
}

Now callers constantly have to check the return value of your functions. What if a function needed to return a value, but then had to indicate an error because mBar wasn't initialised?

You can see the rabbit hole goes quite deep once move away from RAII: resource allocation is initialisation!

From Effective C++ Third Edition - Scott Myers:

Item 4: Make sure that objects are initialized before they're used
...

the responsibility for initialization falls on constructors. The rule there is simple: make sure that all constructors initialize everything in the object

Other advice

You should also prefer to use your constructors initializer list for constructing members:

Foo::Foo() : mBar(new mBar("some", "required", "members"))
{
}

Otherwise you will be making unnecessary copies. I also assume you have a great reason to be using a pointer. As @Niels van Eldik points out in the comments, you should use objects unless you truly need to use a pointer, and in that case you should be using whatever smart pointer best fits your needs (in this case I would verge a guess and say std::unique_ptr:

class Foo
{
public:
Foo() : mBar(std::make_unique<Bar>("some", "required", "members"))
{
}
private:
std::unique_ptr<Bar> mBar;
};

How should I declare default values for instance variables in Python?

Extending bp's answer, I wanted to show you what he meant by immutable types.

First, this is okay:

>>> class TestB():
... def __init__(self, attr=1):
... self.attr = attr
...
>>> a = TestB()
>>> b = TestB()
>>> a.attr = 2
>>> a.attr
2
>>> b.attr
1

However, this only works for immutable (unchangable) types. If the default value was mutable (meaning it can be replaced), this would happen instead:

>>> class Test():
... def __init__(self, attr=[]):
... self.attr = attr
...
>>> a = Test()
>>> b = Test()
>>> a.attr.append(1)
>>> a.attr
[1]
>>> b.attr
[1]
>>>

Note that both a and b have a shared attribute. This is often unwanted.

This is the Pythonic way of defining default values for instance variables, when the type is mutable:

>>> class TestC():
... def __init__(self, attr=None):
... if attr is None:
... attr = []
... self.attr = attr
...
>>> a = TestC()
>>> b = TestC()
>>> a.attr.append(1)
>>> a.attr
[1]
>>> b.attr
[]

The reason my first snippet of code works is because, with immutable types, Python creates a new instance of it whenever you want one. If you needed to add 1 to 1, Python makes a new 2 for you, because the old 1 cannot be changed. The reason is mostly for hashing, I believe.



Related Topics



Leave a reply



Submit