I have a class with a default constructor which initializes many member variables via initializer lists.
I wish to have a parametrized constructor which only updates one of the member variables. A simple example is below, with name
being the only variable that I would like to initialize via the default constructor.
Does it make sense to repeat the initializer list the same as for the default constructor here? Or can I call the default constructor from the parameterized constructor first?
What is the most efficient way to do this?
#include <iostream>
#include<iostream>
#include<fstream>
using namespace std;
class MyClass
{
public:
MyClass() : name("John"),
age(20),
distance(10)
// Many more default initializers
{
}
MyClass(string name)
{
// How to only initialize name with the parameter passed, while everything else still gets the default value like in the default constructor.
}
~MyClass()
{
}
private:
string name;
int age;
int distance;
// Many more
};
I have a class with a default constructor which initializes many member variables via initializer lists.
I wish to have a parametrized constructor which only updates one of the member variables. A simple example is below, with name
being the only variable that I would like to initialize via the default constructor.
Does it make sense to repeat the initializer list the same as for the default constructor here? Or can I call the default constructor from the parameterized constructor first?
What is the most efficient way to do this?
#include <iostream>
#include<iostream>
#include<fstream>
using namespace std;
class MyClass
{
public:
MyClass() : name("John"),
age(20),
distance(10)
// Many more default initializers
{
}
MyClass(string name)
{
// How to only initialize name with the parameter passed, while everything else still gets the default value like in the default constructor.
}
~MyClass()
{
}
private:
string name;
int age;
int distance;
// Many more
};
// How to only initialize name`
This will be a bug. Other values must not be left uninitialized.
Perhaps, you wanted
#include <string>
#include <utility>
class MyClass {
public:
MyClass() : MyClass("John") {} // delegate to the ctor below
explicit MyClass(std::string name) : name(std::move(name)), age(20), distance(10) {}
~MyClass() = default;
private:
std::string name;
int age;
int distance;
};
Or
#include <string>
#include <utility>
class MyClass {
public:
MyClass() = default;
explicit MyClass(std::string name) : name(std::move(name)){}
~MyClass() = default;
private:
std::string name = "John";
int age = 20;
int distance = 10;
};
There are different ways to accomplish this:
#include<iostream>
#include<fstream>
using namespace std;
class MyClass
{
public:
MyClass() : MyClass("John", 20, 10)
{
}
MyClass(string name): MyClass(name, 20, 10)
{
}
~MyClass()
{
}
MyClass(std::string pName, int pAge, int pDistance):
name(pName),
age(pAge),
distance(pDistance)
{}
private:
string name;
int age;
int distance;
// Many more
};
Initialize private members
#include<iostream>
#include<fstream>
using namespace std;
class MyClass
{
public:
MyClass()
{
// using the default values
}
MyClass(string pName): name(name)
{
// update the value of name and keep other set to default
}
~MyClass()
{
}
MyClass(std::string pName, int pAge, int pDistance):
name(pName),
age(pAge),
distance(pDistance)
{
// if needed, you can have another constructor to change the default values
}
private:
string name = std::string("default name");
int age = 0;
int distance = 0;
// Many more
};
The optimum solution depends on your requirements/usage.
For example, if you go for the second option, any change in the default values will trigger a rebuild on all files including the class definition.
My preferred method is aggregate builder pattern:
class MyClass {
public:
struct my_init{
//Aggregate builder class
std::string name = "John";
int age = 20;
int distance = 10;
};
MyClass() = default;
explicit MyClass(my_init val)
: my{std::move(val)} {};
~MyClass() = default;
private:
my_init val;
};
MyClass my_obj0;
//double brackets is the cost:
MyClass my_obj1 {{ .distance=50 }};
This pattern uses named parameters through designated initializer list of aggregate classes. If MyClass
members have differing access specifiers, then a little more complexity is introduced into the class:
class MyClass {
public:
//Aggregate builder class:
struct my_init;//same as above
MyClass()
//delegating constructor call:
: MyClass{my_init{}} {};
explicit MyClass(my_init val)
: name{std::move(val).name}
, age{std::move(val).age}
, dist{std::move(val).distance}
{/*ctor*/};
~MyClass() = default;
private:
std::string name;
protected:
int age;
int dist;
};
This is still much simpler than JAVA style builder pattern.