【c++】vectorに独自クラスを詰めるなら、コピーコンストラクタは明示しよう

c++でよくつかうコレクションは《vector》だね。

で、基本データ型以外にも、独自のデータクラスをvectorに詰めて扱いたいことがある。

そのとき、独自のデータクラスにはコピーコンストラクタを明示しておこう。

よく言われることだけど、再確認。


コード

#testDataClass.h

#pragma once
class testDataClass
{
public:
    // コンストラクタ
    testDataClass();
    testDataClass(int nArr[]);
    // デストラクタ
    virtual    ~testDataClass();
    // コピーコンストラクタ
    testDataClass(const testDataClass& rhs);
    // Get,Set
    int getElement(int nIndex);
    void setElement(int nIndex, int n);

private:
    int m_nArr[2];
};

#testDataClass.cpp

#include "stdafx.h"
#include "testDataClass.h"

// コンストラクタ
testDataClass::testDataClass()
{
    for (int nIndex = 0; nIndex < (sizeof(m_nArr) / sizeof(*m_nArr)); nIndex++)
    {
        m_nArr[nIndex] = 0;
    }
    TRACE("---Constructor---\n");
}
testDataClass::testDataClass(int nArrArg[])
{
    for (int nIndex = 0; nIndex < (sizeof(m_nArr) / sizeof(*m_nArr)); nIndex++)
    {
        if (nArrArg[nIndex] != NULL)
        {
            m_nArr[nIndex] = nArrArg[nIndex];
        }
    }
    TRACE("---Constructor---\n");
}

// デストラクタ
testDataClass::~testDataClass()
{
    TRACE("---Destructor---\n");
}

// コピーコンストラクタ
testDataClass::testDataClass(const testDataClass& rhs)
{
    for (int nIndex = 0; nIndex < (sizeof(m_nArr) / sizeof(*m_nArr)); nIndex++)
    {
        m_nArr[nIndex] = rhs.m_nArr[nIndex];
    }
    TRACE("---CopyConstructor---※今回のポイント\n");
}

// Getter
int testDataClass::getElement(int nIndex)
{
    return m_nArr[nIndex];
}

// Setter
void testDataClass::setElement(int nIndex, int n)
{
    m_nArr[nIndex] = n;
}

#testClass.h

#pragma once
class testClass
{
public:
    testClass();
    ~testClass();
};

#testClass.cpp

#include "stdafx.h"
#include "testClass.h"
#include "testDataClass.h"
#include <vector>
#include <iostream>

testClass::testClass()
{
    std::vector<testDataClass> vTest;

    // vecterに追加するオブジェクトに渡す値
    int    nArr1[2] = { 1, 11 };
    int    nArr2[2] = { 2, 12 };
    int    nArr3[2] = { 3, 13 };
    int    nArr4[2] = { 4, 14 };

    // vecterにオブジェクトを追加
    // ※ココで testDataClass のコピーコンストラクタも走る
    vTest.push_back(testDataClass(nArr1));
    vTest.push_back(testDataClass(nArr2));
    vTest.push_back(testDataClass(nArr3));
    vTest.push_back(testDataClass(nArr4));

    // vecterに追加したオブジェクトが持つ値を全て表示
    for (int nIndex = 0; nIndex < vTest.size(); nIndex++)
    {
        for (int nIndexSub = 0; nIndexSub < 2; nIndexSub++)
        {
            TRACE("→%d\n", vTest.at(nIndex).getElement(nIndexSub));
        }
        TRACE("----------\n");
    }
}

testClass::~testClass()
{
}


出力

testDataClass.cpp(22) : atlTraceGeneral - ---Constructor---
testDataClass.cpp(38) : atlTraceGeneral - ---CopyConstructor---※今回のポイント
testDataClass.cpp(28) : atlTraceGeneral - ---Destructor---
testDataClass.cpp(22) : atlTraceGeneral - ---Constructor---
testDataClass.cpp(38) : atlTraceGeneral - ---CopyConstructor---※今回のポイント
testDataClass.cpp(28) : atlTraceGeneral - ---Destructor---
testDataClass.cpp(38) : atlTraceGeneral - ---CopyConstructor---※今回のポイント
testDataClass.cpp(28) : atlTraceGeneral - ---Destructor---
testDataClass.cpp(22) : atlTraceGeneral - ---Constructor---
testDataClass.cpp(38) : atlTraceGeneral - ---CopyConstructor---※今回のポイント
testDataClass.cpp(38) : atlTraceGeneral - ---CopyConstructor---※今回のポイント
testDataClass.cpp(28) : atlTraceGeneral - ---Destructor---
testDataClass.cpp(28) : atlTraceGeneral - ---Destructor---
testDataClass.cpp(38) : atlTraceGeneral - ---CopyConstructor---※今回のポイント
testDataClass.cpp(28) : atlTraceGeneral - ---Destructor---
testDataClass.cpp(22) : atlTraceGeneral - ---Constructor---
testDataClass.cpp(38) : atlTraceGeneral - ---CopyConstructor---※今回のポイント
testDataClass.cpp(38) : atlTraceGeneral - ---CopyConstructor---※今回のポイント
testDataClass.cpp(38) : atlTraceGeneral - ---CopyConstructor---※今回のポイント
testDataClass.cpp(28) : atlTraceGeneral - ---Destructor---
testDataClass.cpp(28) : atlTraceGeneral - ---Destructor---
testDataClass.cpp(28) : atlTraceGeneral - ---Destructor---
testDataClass.cpp(38) : atlTraceGeneral - ---CopyConstructor---※今回のポイント
testDataClass.cpp(28) : atlTraceGeneral - ---Destructor---
testClass.cpp(29) : atlTraceGeneral - →1
testClass.cpp(29) : atlTraceGeneral - →11
testClass.cpp(31) : atlTraceGeneral - ----------
testClass.cpp(29) : atlTraceGeneral - →2
testClass.cpp(29) : atlTraceGeneral - →12
testClass.cpp(31) : atlTraceGeneral - ----------
testClass.cpp(29) : atlTraceGeneral - →3
testClass.cpp(29) : atlTraceGeneral - →13
testClass.cpp(31) : atlTraceGeneral - ----------
testClass.cpp(29) : atlTraceGeneral - →4
testClass.cpp(29) : atlTraceGeneral - →14
testClass.cpp(31) : atlTraceGeneral - ----------
testDataClass.cpp(28) : atlTraceGeneral - ---Destructor---
testDataClass.cpp(28) : atlTraceGeneral - ---Destructor---
testDataClass.cpp(28) : atlTraceGeneral - ---Destructor---
testDataClass.cpp(28) : atlTraceGeneral - ---Destructor---

と、まぁこんな感じで、vectorに独自のデータクラスをpush_backするとき、

要素の追加・並び替えのためにコピーコンストラクタが走る。

    // vecterにオブジェクトを追加
    // ※ココで testDataClass のコピーコンストラクタも走る
    vTest.push_back(testDataClass(nArr1));
    vTest.push_back(testDataClass(nArr2));
    vTest.push_back(testDataClass(nArr3));
    vTest.push_back(testDataClass(nArr4));

ここのときね。


ま、コピーコンストラクタは明示しなくても"自動生成"される。

とはいえ。

独自のデータクラスで自動生成されたコピーコンストラクタを、vectorの処理で多用されるのは思わぬエラーの温床。

キッチリ、明示しておこう。

これマジ。