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

Viewed 297

Following a tutorial on learncpp.com which discusses declaring member variables outside of constructors. However in previous lessons the author mentions that minimizing the amount of constructors is optimal and can be done by using default values in the parameters of a constructor. This is confusing to me because suddenly there are two places to give default values, directly where you define your member variable, and in the parameter of the constructor. Additionally, it seemed like the whole point of defining default values outside of the constructor was to prevent redundant code because "if you update the default value for a member, you need to touch each constructor".

Here's the example used:

class Ball
{
  private:
    std::string m_color{ "black" }; //default value "black" declared here
    double m_radius{};
 
  public:
    Ball(double radius)
        : m_radius{ radius } {}
 
    Ball(const std::string& color = "black", double radius = 10.0) //and here!
        : m_color{ color },
          m_radius{ radius } {}
 
    void print()
    {
        std::cout << "color: " << m_color << ", radius: " << m_radius << '\n';
    }
};

It seems like the solution given by the tutorial uses "black" in two places, contradicting the idea of having all default values in one place to minimize changing constructors. I'm wondering what the best practice here is, and if there is a better way to write this class.

1 Answers

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.

Related