Consider the following line of code:
int x = 5;
This statement uses copy initialization to initialize newly created integer variable x to the value of 5.
However, classes are a little more complicated, since they use constructors for initialization. This lesson will examine topics related to copy initialization for classes.
Given our Fraction class:
#include <cassert>
#include <iostream>
class Fraction
{
private:
int m_numerator;
int m_denominator;
public:
// Default constructor
Fraction(int numerator=0, int denominator=1) :
m_numerator(numerator), m_denominator(denominator)
{
assert(denominator != 0);
}
friend std::ostream& operator<<(std::ostream& out, const Fraction &f1);
};
std::ostream& operator<<(std::ostream& out, const Fraction &f1)
{
out << f1.m_numerator << "/" << f1.m_denominator;
return out;
}
Consider the following:
int main()
{
Fraction six = Fraction(6);
std::cout << six;
return 0;
}
If you were to compile and run this, you’d see that it produces the expected output:
6/1
This form of copy initialization is evaluated the same way as the following:
Fraction six(Fraction(6));
And as you learned in the previous lesson, this can potentially make calls to both Fraction(int, int) and the Fraction copy constructor (which may be elided for performance reasons). However, because eliding isn’t guaranteed (prior to C++17, where elision in this particular case is now mandatory), it’s better to avoid copy initialization for classes, and use uniform initialization instead.
Rule: Avoid using copy initialization, and use uniform initialization instead.
Other places copy initialization is used
There are a few other places copy initialization is used, but two of them are worth mentioning explicitly. When you pass or return a class by value, that process uses copy initialization.
Consider: