13.16 Shallow vs. deep copying


https://www.learncpp.com/cpp-tutorial/shallow-vs-deep-copying/

Shallow copying


C++は私たちのクラスを知らないからです.
default copy constructorとdefault付与オペレータ
memberwise copy(shallow copyとも呼ばれる)を行う
これは、c++がすべてのメンバーをコピーしてインポートすることを意味します.
クラスが簡単であれば、これは大きな問題ではありません.
ただし、メモリを動的に割り当てるクラスであれば、物語は異なります.
#include <cstring> // for strlen()
#include <cassert> // for assert()

class MyString
{
private:
    char* m_data{};
    int m_length{};

public:
    MyString(const char* source = "" )
    {
        assert(source); // make sure source isn't a null string

        // Find the length of the string
        // Plus one character for a terminator
        m_length = std::strlen(source) + 1;

        // Allocate a buffer equal to this length
        m_data = new char[m_length];

        // Copy the parameter string into our internal buffer
        for (int i{ 0 }; i < m_length; ++i)
            m_data[i] = source[i];
    }

    ~MyString() // destructor
    {
        // We need to deallocate our string
        delete[] m_data;
    }

    char* getString() { return m_data; }
    int getLength() { return m_length; }
};
上記のダイナミックメモリの処理に浅いコピーを使用する場合
pointerの共有に伴い、思わぬ動作が発生する可能性があります
また、最後にメモリを解放すると、解放されたメモリが解放されるため、クラッシュが発生します.

Deep copying


この場合、答えはdeepcopyです
deepcopyは、copyにメモリを割り当て、実際の値のみをコピーします.
// assumes m_data is initialized
void MyString::deepCopy(const MyString& source)
{
    // first we need to deallocate any value that this string is holding!
    delete[] m_data;

    // because m_length is not a pointer, we can shallow copy it
    m_length = source.m_length;

    // m_data is a pointer, so we need to deep copy it if it is non-null
    if (source.m_data)
    {
        // allocate memory for our copy
        m_data = new char[m_length];

        // do the copy
        for (int i{ 0 }; i < m_length; ++i)
            m_data[i] = source.m_data[i];
    }
    else
        m_data = nullptr;
}

// Copy constructor
MyString::MyString(const MyString& source)
{
    deepCopy(source);
}
As you can see, this is quite a bit more involved than a simple shallow copy! First, we have to check to make sure source even has a string (line 11). If it does, then we allocate enough memory to hold a copy of that string (line 14). Finally, we have to manually copy the string (lines 17 and 18).
Now let’s do the overloaded assignment operator. The overloaded assignment operator is slightly trickier:
// Assignment operator
MyString& MyString::operator=(const MyString& source)
{
    // check for self-assignment
    if (this != &source)
    {
        // now do the deep copy
        deepCopy(source);
    }

    return *this;
}
付与オペレータはcopyコンストラクション関数とよく似ていますが、3つの主な違いがあります.
  • We added a self-assignment check.
  • We return *this so we can chain the assignment operator.
  • We need to explicitly deallocate any value that the string is already holding (so we don’t have a memory leak when m_data is reallocated later).
  • A better solution


    実際、stringをcharpointerで直接処理するよりも、
    std::stringを使用すると、問題を簡単に解決できます.
    std::string、vectorはメモリ管理をサポートします.
    メモリを直接管理しないで、既存のライブラリを使用したほうがいいです.