オブジェクトのコピー 解答ページ | Programming Place Plus 新C++編

トップページ新C++編オブジェクトのコピー

このページの概要 🔗

このページは、練習問題の解答例や解説のページです。



解答・解説 🔗

問題1 (確認★) 🔗

オブジェクトをコピーすることを禁止するにはどうすればいいですか?


オブジェクトをコピーする方法として、コピーコンストラクタ(本編解説)とコピー代入演算子(本編解説)があります。そのためこの2つが使えないようにすれば、オブジェクトのコピーを禁止できます。

コピーコンストラクタもコピー代入演算子も、コンパイラが暗黙的に生成することがある特殊なメンバ関数の一種なので、=delete を使って削除することができます(「コンストラクタ」「静的メンバ」のページを参照)。

#include <iostream>

class DataStore {
public:
    explicit DataStore(int v) : m_value {v}
    {}

    // コピーコンストラクタを削除
    DataStore(const DataStore& other) = delete;

    // コピー代入演算子を削除
    DataStore& operator=(const DataStore& rhs) = delete;

private:
    int    m_value;
};

int main()
{
    DataStore ds1 {100};
    DataStore ds2 {ds1};  // エラー。コピーコンストラクタは削除されている
    ds2 = ds1;            // エラー。コピー代入演算子は削除されている
}

実行結果:

問題2 (応用★★) 🔗

ディープコピーを実装した MyValueクラスのサンプルプログラムを、各種の等価演算子と関係演算子が、ポインタの先にある値による比較になるように実装してください。


演算子のオーバーロード」のページで解説したように、6種類の演算子をオーバーロードします。

#include <iostream>

class MyValue {
public:
    explicit MyValue(int v);
    MyValue(const MyValue& other);
    ~MyValue();

    MyValue& operator=(const MyValue& rhs);

    inline void set(int v)
    {
        *m_pvalue = v;
    }

    inline int get() const
    {
        return *m_pvalue;
    }

    inline bool operator==(const MyValue& other) const
    {
        return *m_pvalue == *other.m_pvalue;
    }
    inline bool operator!=(const MyValue& other) const
    {
        return !(*this == other);
    }
    inline bool operator<(const MyValue& other) const
    {
        return *m_pvalue < *other.m_pvalue;
    }
    inline bool operator>(const MyValue& other) const
    {
        return !(*this <= other);
    }
    inline bool operator<=(const MyValue& other) const
    {
        return (*this < other) || (*this == other);
    }
    inline bool operator>=(const MyValue& other) const
    {
        return !(*this < other);
    }

private:
    int*  m_pvalue;
};

MyValue::MyValue(int v) : m_pvalue{new int {v}}
{
}

MyValue::MyValue(const MyValue& other) : m_pvalue{new int {*other.m_pvalue}}
{
}

MyValue::~MyValue()
{
    delete m_pvalue;
}

MyValue& MyValue::operator=(const MyValue& rhs)
{
    if (this != &rhs)
    {
        // 以前のオブジェクトを解放して、新しいオブジェクトを生成する
        delete m_pvalue;
        m_pvalue = new int{*rhs.m_pvalue};
    }
    return *this;
}


int main()
{
    MyValue vp1(100);

    MyValue vp2{vp1};
    vp2.set(300);

    std::cout << std::boolalpha << (vp1 == vp2) << "\n"
        << (vp1 != vp2) << "\n"
        << (vp1 < vp2) << "\n"
        << (vp1 > vp2) << "\n"
        << (vp1 <= vp2) << "\n"
        << (vp1 >= vp2) << "\n";
}

実行結果:

false
true
true
false
true
false

問題3 (応用★★) 🔗

ディープコピーを、専用のメンバ関数を作成する方法で実装してください。


問題② のプログラムを改造してみます。

#include <iostream>

class MyValue {
public:
    explicit MyValue(int v);
    MyValue(const MyValue& other) = delete;
    ~MyValue();

    MyValue& operator=(const MyValue& rhs) = delete;

    inline void set(int v)
    {
        *m_pvalue = v;
    }

    inline int get() const
    {
        return *m_pvalue;
    }

    inline bool operator==(const MyValue& other) const
    {
        return *m_pvalue == *other.m_pvalue;
    }
    inline bool operator!=(const MyValue& other) const
    {
        return !(*this == other);
    }
    inline bool operator<(const MyValue& other) const
    {
        return *m_pvalue < *other.m_pvalue;
    }
    inline bool operator>(const MyValue& other) const
    {
        return !(*this <= other);
    }
    inline bool operator<=(const MyValue& other) const
    {
        return (*this < other) || (*this == other);
    }
    inline bool operator>=(const MyValue& other) const
    {
        return !(*this < other);
    }

    MyValue* deep_copy() const;

private:
    int*  m_pvalue;
};

MyValue::MyValue(int v) : m_pvalue{new int {v}}
{
}

MyValue::~MyValue()
{
    delete m_pvalue;
}

MyValue* MyValue::deep_copy() const
{
    return new MyValue(*m_pvalue);
}


int main()
{
    MyValue vp1(100);

    MyValue* vp2{vp1.deep_copy()};
    vp2->set(300);

    std::cout << std::boolalpha << (vp1 == *vp2) << "\n"
        << (vp1 != *vp2) << "\n"
        << (vp1 < *vp2) << "\n"
        << (vp1 > *vp2) << "\n"
        << (vp1 <= *vp2) << "\n"
        << (vp1 >= *vp2) << "\n";

    delete vp2;
}

実行結果:

false
true
true
false
true
false

ディープコピー用のメンバ関数 deep_copy を作成し、代わりにコピーコンストラクタとコピー代入演算子を =delete で削除しています。

deep_copyメンバ関数では、new式(本編解説)によって MyValueクラスの動的オブジェクトを生成し、データメンバを初期化してから return しています。

動的オブジェクトを呼び出し元に返すために、ポインタ型のまま扱うことになる点がデメリットとなります。いちいち、間接参照を行う必要があったり、なにより最後の delete を忘れる可能性がある点が問題になりそうです。

【上級】delete については、std::unique_ptr を使うなどの方法で解消できます。


参考リンク 🔗



更新履歴 🔗




はてなブックマーク に保存 Pocket に保存 Facebook でシェア
X で ポストフォロー LINE で送る noteで書く
rss1.0 取得ボタン RSS 管理者情報 プライバシーポリシー
先頭へ戻る