13.16 Shallow vs. deep copying

주홍영·2022년 3월 18일
0

Learncpp.com

목록 보기
159/199

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

Shallow copying

c++이 우리의 클래스를 자세히 알지 못하기 때문에
default copy constructor와 default assignment operator는
memberwise copy (also known as a Shallow copy)를 한다
이는 c++은 모든 member를 똑같이 복사해서 가져온다는 의미이다
클래스가 간단한 경우에는 이는 크게 문제가 되지 않는다

하지만 만약 dynamically allocated memory를 사용하는 클래스의 경우 이야기가 달라진다

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

위와 같이 dynamic memory를 다룰 때 shallow copy를 하게 되면
pointer가 공유되면서 의도치 않은 동작이 발생할 수도 있다

그리고 마지막에 메모리를 해제하면 이미 해제된 메모리를 해제하므로 크래쉬가 날 것이다

Deep copying

이러한 경우에 해답이 deep copy 이다
deep copy는 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;
}

assignment operator가 copy constructor와 매우 비슷해 보이지만 세가지 주요 차이점이 있다

  • 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

사실 직접 char pointer를 사용해서 string을 다루기보다
std::string을 사용하면 편히 해결되는 문제다
std::string, vector의 경우에 memory management를 지원하고 있으므로
직접 메모리 관리를 하면서까지 만들지 말고 있는 library를 잘 사용하는 것도 방법이다

profile
청룡동거주민

0개의 댓글