Open-Systems-Pharmacology / OSPSuite.FuncParser

Function parser for numeric and logic expressions used by the Open Systems Pharmacology Suite
Other
0 stars 3 forks source link

Always use constructor member initializer list #55

Open IndrajeetPatil opened 2 years ago

IndrajeetPatil commented 2 years ago

Although member initializer lists may seem like syntactic sugar, it is not. They make a functional difference, especially when parameters take class instances as inputs.

Here is example code:

#include <iostream>

class Species
{
public:
    Species()
    {
        std::cout << "Created species!" << std::endl;
    };
};

// without member initializer list ---------------

class Entity1
{
public:
    Entity1(Species species)
    {
        _species = species;
    };

private:
    Species _species;
};

// with member initializer list ---------------

class Entity2
{
public:
    Entity2(Species species) : _species{species} {};

private:
    Species _species;
};

int main()
{
    std::cout << "without member initializer list" << std::endl;
    Species human;
    Entity1 Entity1(human);

    std::cout << std::endl;

    std::cout << "with member initializer list" << std::endl;
    Species rat;
    Entity2 Entity2(rat);

    return 0;
}

This will produce the following output:

without member initializer list
Created species!
Created species!

with member initializer list
Created species!

Note that the constructor for the class without member initializer list creates two instances of Species!

Once here:

private:
    Species _species;

And another time here:

    Entity1(Species species)
    {
        _species = species;
    };

This is an unnecessary waste of performance, and can be easily avoided by adopting constructor member initializer list usage as a good practice.

This might seem irrelevant if the class constructor has only parameters of primitive types, but it's important nonetheless to adopt good practices, and the whole point of C++ is not to discriminate between primitive versus user defined types. 🙃

msevestre commented 2 years ago

This is so messed up. Why would a constructor be called twice when you only instantiate a class once. The whole point of this thread should be to never use C++ again :)

IndrajeetPatil commented 2 years ago

Hey man, don't rain on my parade! 😁

Let me wallow in the newfound C++ excitement, at least for a bit. 😅

Yuri05 commented 2 years ago

Just to make the picture complete:

#include <iostream>

using namespace std;

class Species
{
public:
    Species()
    {
        cout << "Created species!" << endl;
    }

    Species(const Species & species)
    {
        cout << "Copy constructor species" << endl;
    }

    Species & operator = (const Species & species)
    {
        cout << "Assignment operator species" << endl;
        return *this;
    }
};

// without member initializer list ---------------

class Entity1
{
public:
    Entity1(const Species & species)
    {
        _species = species;
    };

private:
    Species _species;
};

// with member initializer list ---------------

class Entity2
{
public:
    Entity2(const Species & species) : _species{species} {};

private:
    Species _species;
};

int main()
{
    Species species;

    cout << endl << "====== without member initializer list" << endl;
    Entity1 Entity1(species);

    cout << endl << "====== with member initializer list" << endl;
    Entity2 Entity2(species);

    return 0;
}

Output:

Created species!

====== without member initializer list
Created species!
Assignment operator species

====== with member initializer list
Copy constructor species

So yes, member initializer is more efficient for constructor parameters of type class (1 copy constructor vs. 1 constructor + 1 assignment operator).

IndrajeetPatil commented 2 years ago

For future me, there is an exhaustive argument for why one ought to always be using member initializer list here:

Item 4 in Effective C++: 55 Specific Ways to Improve Your Programs and Designs (3rd Edition) by Scott Meyers.