[C++] Rule of Five

haeryong·2023년 6월 7일
0

Rule of Five : 클래스에서 동적 할당 메모리를 사용하고 있다면 소멸자, 복제 생성자, 이동 생성자, 복제 대입 연산자, 이동 대입 연산자를 반드시 구현한다.

Rule of Zero : 모던 C++로 작성할 경우 raw pointer 대신 STL의 std::vector나 스마트 포인터를 사용해 위의 5가지를 구현하지 않아도 괜찮도록 작성하는 것을 권장한다.

0. base class

raw pointer를 이용한 matrix 클래스를 사용한다.

template<typename T>
class Matrix
{
public:
    Matrix(size_t height, size_t width)
        : mHeight(height), mWidth(width)
    {
        mData = new T * [mHeight];
        for (size_t i = 0; i < mHeight; ++i)
        {
            mData[i] = new T[mWidth];
        }
    }
    T& at(size_t row, size_t col) { return mData[row][col]; }
private:
    size_t mHeight = 0;
    size_t mWidth = 0;
    T** mData = nullptr;
};

1. 소멸자

base class의 경우 memory leak이 발생한다. 소멸자에서 mData에 할당된 메모리를 해제해주어야 한다.

    ~Matrix()
    {
        clear();
    }
    
    void clear() noexcept
    {
        for (size_t i = 0; i < mHeight; ++i)
        {
            delete[] mData[i];
        }
        delete[] mData;
        mData = nullptr;
        mHeight = 0;
        mWidth = 0;
    }

2. 복제 생성자

  • 컴파일러가 자동으로 생성하는 복제 생성자를 사용할 경우 memory leak의 위험이 있다.(orphan)
    Matrix(const Matrix& src)
        : Matrix(src.mHeight, src.mWidth)
    {
        for (size_t row = 0; row < mHeight; ++row)
        {
            for (size_t col = 0; col < mWidth; ++col)
            {
                mData[row][col] = src.mData[row][col];
            }
        }
    }
  • 예시
Matrix<int> mat1(3, 3);
Matrix<int> mat2(mat1); // mat1과 동일한 값(deep copy)을 가지는 mat2가 생성됨.

3. 복제 대입 연산자

  • 작성한 복제 생성자를 이용해 안전한 대입 연산자를 작성한다.
    Matrix& operator=(const Matrix& rhs)
    {
        if (this == &rhs)
        {
            return *this;
        }
        Matrix temp(rhs);
        swap(*this, temp);
        return *this;
    }
    
    void swap(Matrix& first, Matrix& second) noexcept
    {
        std::swap(first.mHeight, second.mHeight);
        std::swap(first.mWidth, second.mWidth);
        std::swap(first.mData, second.mData);
    }
  • 예시
Matrix<int> mat1(3, 3);
auto mat2 = mat1;

4. 이동 생성자

    Matrix(Matrix&& src) noexcept
    {
        moveFrom(src);
    }
    
    void moveFrom(Matrix& src) noexcept
    {
        mHeight = src.mHeight;
        mWidth = src.mWidth;
        mData = src.mData;

        src.mHeight = 0;
        src.mWidth = 0;
        src.mData = nullptr;
    }
  • 예시
Matrix<int> mat(5, 5);
auto mat2(std::move(mat));

5. 이동 대입 연산자

    Matrix& operator=(Matrix&& rhs) noexcept
    {
        if (this == &rhs)
        {
            return *this;
        }
        clear();
        moveFrom(rhs);
        return *this;
    }
  • 예시
Matrix<int> mat(5, 5);
auto mat2 = std::move(mat);

6. 전체 코드

template<typename T>
class Matrix
{
public:
    Matrix(size_t height, size_t width)
        : mHeight(height), mWidth(width)
    {
        mData = new T * [mHeight];
        for (size_t i = 0; i < mHeight; ++i)
        {
            mData[i] = new T[mWidth];
        }
    }
    // 1. 소멸자
    ~Matrix()
    {
        clear();
    }
    // 2. 복제 생성자
    Matrix(const Matrix& src)
        : Matrix(src.mHeight, src.mWidth)
    {
        for (size_t row = 0; row < mHeight; ++row)
        {
            for (size_t col = 0; col < mWidth; ++col)
            {
                mData[row][col] = src.mData[row][col];
            }
        }
    }
    // 3. 복제 대입 연산자
    Matrix& operator=(const Matrix& rhs)
    {
        if (this == &rhs)
        {
            return *this;
        }
        Matrix temp(rhs);
        swap(*this, temp);
        return *this;
    }
    // 4. 이동 생성자
    Matrix(Matrix&& src) noexcept
    {
        moveFrom(src);
    }
    // 5. 이동 대입 연산자
    Matrix& operator=(Matrix&& rhs) noexcept
    {
        if (this == &rhs)
        {
            return *this;
        }
        clear();
        moveFrom(rhs);
        return *this;
    }

    T& at(size_t row, size_t col) { return mData[row][col]; }

private:
    void swap(Matrix& first, Matrix& second) noexcept
    {
        std::swap(first.mHeight, second.mHeight);
        std::swap(first.mWidth, second.mWidth);
        std::swap(first.mData, second.mData);
    }

    void clear() noexcept
    {
        for (size_t i = 0; i < mHeight; ++i)
        {
            delete[] mData[i];
        }
        delete[] mData;
        mData = nullptr;
        mHeight = 0;
        mWidth = 0;
    }

    void moveFrom(Matrix& src) noexcept
    {
        mHeight = src.mHeight;
        mWidth = src.mWidth;
        mData = src.mData;

        src.mHeight = 0;
        src.mWidth = 0;
        src.mData = nullptr;
    }

    size_t mHeight = 0;
    size_t mWidth = 0;
    T** mData = nullptr;
};

0개의 댓글