13.12 The copy constructor


https://www.learncpp.com/cpp-tutorial/the-copy-constructor/

Recapping the types of initialization


初期化については、以下のレッスンで説明します.
まずc++がサポートする初期化についてまとめます
direct init、uniform init、copy initなどがあります
サンプルコードを使用して表示
#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;
}
私たちは次のようにdirect initを行うことができます.
int x(5); // Direct initialize an integer
Fraction fiveThirds(5, 3); // Direct initialize a Fraction, calls Fraction(int, int) constructor
c++11で統合initが可能
int x { 5 }; // Uniform initialization of an integer
Fraction fiveThirds {5, 3}; // Uniform initialization of a Fraction, calls Fraction(int, int) constructor
最後にinitをコピーすることもできます
int x = 6; // Copy initialize an integer
Fraction six = Fraction(6); // Copy initialize a Fraction, will call Fraction(6, 1)
Fraction seven = 7; // Copy initialize a Fraction.  The compiler will try to find a way to convert 7 to a Fraction, which will invoke the Fraction(7, 1) constructor.

The copy constructor

#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;
}

int main()
{
	Fraction fiveThirds { 5, 3 }; // Direct initialize a Fraction, calls Fraction(int, int) constructor
	Fraction fCopy { fiveThirds }; // Direct initialize -- with what constructor?
	std::cout << fCopy << '\n';
}
上のコードからfivethirdは普通のdirect initであることがわかります
クラスで定義されているFraction(int,int)コンストラクタを呼び出します
ではfCopyの場合は何でしょうか.
このケースに注目しなければなりません
fCopyの場合、同じ分割タイプのFiveThirdsオブジェクトをパラメータで渡します.
これをcopy構造関数と呼ぶ.これは既存のobjectを用いてコンストラクション関数を起動する場合である.一般的には、copyコンストラクション関数を作成しない限り、C++はデフォルトバージョンです.
私たちのニーズに合えばdefault copy constructorを使うのも大きな問題ではありません.
#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);
    }

    // Copy constructor
    Fraction(const Fraction& fraction)
        : m_numerator{fraction.m_numerator}, m_denominator{fraction.m_denominator}
        // Note: We can access the members of parameter fraction directly, because we're inside the Fraction class
    {
        // no need to check for a denominator of 0 here since fraction must already be a valid Fraction
        std::cout << "Copy constructor called\n"; // just to prove it works
    }

    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;
}

int main()
{
	Fraction fiveThirds { 5, 3 }; // Direct initialize a Fraction, calls Fraction(int, int) constructor
	Fraction fCopy { fiveThirds }; // Direct initialize -- with Fraction copy constructor
	std::cout << fCopy << '\n';
}
上のコードはcopy構造関数の例です
計画どおりに実施すればよい
ここで注意したいのは、copy constructorでは同じオブジェクトではありませんが、スコアのprivate member変数にアクセスできます.つまり、同じタイプの場合、同じオブジェクトでなくても互いのプライベートメンバー変数にアクセスできます.

Preventing copies


copy constructorをprivateに設定するとコンパイルエラーが発生します
しかし、現実にはこのような理由はない.

The copy constructor may be elided

#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);
    }

        // Copy constructor
	Fraction(const Fraction &fraction)
		: m_numerator{fraction.m_numerator}, m_denominator{fraction.m_denominator}
	{
		// no need to check for a denominator of 0 here since fraction must already be a valid Fraction
		std::cout << "Copy constructor called\n"; // just to prove it works
	}

	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;
}

int main()
{
	Fraction fiveThirds { Fraction { 5, 3 } };
	std::cout << fiveThirds;
	return 0;
}
main関数がfiveThirdsをanonymous Fractionオブジェクトに初期化していることがわかります.もしそうならcopyコンストラクション関数を実行する予定ですが、実際には
c++で省略し、
Fraction fiveThirds{ 5, 3 };
elisionと呼ぶ