[Basic C++] #3_얕은 복제, 깊은 복제
C++ 개발 중 객체 생성, 복제, 그리고 대입 과정에서 발생하는,
얕은 복제와 깊은 복제에 대해 알아보겠습니다.
#0. 얕은 복제
1. 복제 생성자, 얕은 복제
- 얕은 복제는 클래스가 기본적으로 제공하는 디폴트 복사 생성자 혹은 디폴트 대입 연산자를 활용해 원본 값과 복사된 값이 같은 참조를 가리키도록 합니다.
2. 코드 예제(복사 생성자)
#include "Spreadsheet.h"
void printSpreadsheet(Spreadsheet s);
int main()
{
Spreadsheet s1(4, 3); // 가로 4, 세로 3의 스프레드 시트 생성
printSpreadsheet(s1);
}
Details
"s1" 객체가 printSpreadsheet 함수의 인수로 전달되어, 임시의 "s"객체로 복제됩니다. 이때, 발생하는 복제의 유형은 "얕은 복제"로, 포이터가 가리키는 데이터가 복제되지 않고, 피상 적인 그 변숫값, 즉 참조 값 혹은 주소값만 복제됩니다.
Details
따라서, printSpreadsheet 함수의 종료 시점에 "s"객체가 소멸되면, 해당 객체가 가리키고 있던 메모리가 반환되어 "s1" 객체가 이미 반환되어 빈 메모리를 가리키고 있는 현상이 발생할 수 있습니다.
3. 코드 예제(대입 연산자)
int main ()
{
Spreadsheet s1(2,2), s2(3,3);
s1 = s2;
}
Details
대입 과정에서도 문제가 발생할 수 있습니다. 별도로 생성된 "s1" 객체와 "s2" 객체는 독립적으로 동적 할당받은 메모리 영역이 존재합니다. 하지만, 대입 연산이 호출되며, s2객체는 s1객체의 메모리 영역을 가리키게 되면서, 최초로 할당받은 메모리 영역이 주인을 잃어버리게 됩니다. 이러한 문제들이 발생하는 것을 방지하기 위해, 우리는 사용자 정의 복제 생성자와 대입 연산자가 필요해 보입니다.
#1. 깊은 복사
1. 깊은 복사?
- 깊은 복사는 사용자 정의 복제 생성자와 대입 연산자를 통해 새로운 메모리 공간을 할당받아 원본 객체를 복사합니다.
2. 코드 예제
class Spreadsheet
{
public:
Spreadsheet(int inWidth, int inHeight);
Spreadsheet(const Spreadsheet& src); // 사용자 정의 복사 생성자
Spreadsheet& operator =(const Spreadsheet* rhs); // 사용자 정의 대입 연산자
...
};
#include "Spreadsheet.h"
void Spreadsheet::Spreadsheet(const Spreadsheet& src)
{
mWidth = src.mWidth;
mHeight = src.mHeight;
/*
* 2차원 배열의 동적 메모리 할당
*/
mCells = new SpreadsheetCell* [mWidth];
for(int i=0; i<mWidth; i++)
mCells[i] = new SpreadsheetCell[mHeight];
// 2차원 배열의 복사
for(int i=0; i<mWidth; i++)
for(int j=0; j<mHeight; j++)
mCells[i][j] = src.mCells[i][j];
}
Spreadsheet& Spreadsheet::operator=(const Spreadsheet& rhs)
{
if(this == &rhs) return *this;
// 기존의 동적 메모리 해제
for(int i=0; i<mWidth; i++)
delete[] mCells[i];
delete[] mCells;
mCells = nullptr;
mWidth = rhs.mWidth;
mHeight = rhs.mHeight;
// 새로운 동적 메모리 할당
mCells = new SpreadsheetCell* mCells[mWidth];
for(int i=0; i<mWidth; i++)
mCells[i] = new Spreadsheetcell[mHeight];
// 깊은 복사
for(int i=0; i<mWidth; i++)
for(int j=0; j<mHeight; j++)
mCells[i][j] = rhs.mCells[i][j];
}
Details
사용자 정의 대입 연사자 정의의 특징은 두 가지 입니다. 하나는 자기 대입 연산인지 확인하는 것과 기존에 동적으로 할당받은 메모리의 해제 및 새로운 동적 메모리 할당입니다.
#2. 추가 예제
class Spreadsheet
{
public:
Spreadsheet(int inWidth, int inHeight);
Spreadsheet(const Spreadsheet& src) = delete;
~Spreadsheet();
Spreadsheet& operator =(const Spreadsheet* rhs) = delete;
...
};
Details
delete 키워드를 사용해 객체의 복사 생성자 혹은 대입 연산자의 호출을 미연에 방지하는 방법도 존재합니다.
'언어 > Basic C++' 카테고리의 다른 글
[Basic C++] #6_오버라이딩과 오버로딩의 차이점 (0) | 2022.03.07 |
---|---|
[Basic C++] #5 메서드 종류, static 메서드, const 메서드 (0) | 2022.03.07 |
[Basic C++] #4 데이터 멤버의 종류, static, const (0) | 2022.03.07 |
[Basic C++] #2 C 스타일의 문자열, char*, const char* (0) | 2022.02.27 |
[Basic C++] #1 Map, Unordered_map, 해쉬 테이블 (0) | 2022.02.23 |