깊은 복사(Deep Copy)와 얕은 복사(Shallow Copy)
2024. 3. 20. 14:38ㆍ프로그래밍/C++
깊은 복사는 복사에 의해 실제로 두 개의 값이 생성되는 것이다.
얕은 복사는 대상이 되는 값은 하나뿐인데 접근 포인터가 두 개로 느는 것이다.
얕은 복사의 문제점을 살펴보자
#include <iostream>
using namespace std;
int main()
{
int* pA, * pB;
pA = new int;
*pA = 10;
pB = new int;
pB = pA;
cout << *pA << endl;
cout << *pB << endl;
delete pA;
delete pB;
return 0;
}
main
pA |
0xFFF000BD0
pointer to int
0x5C7BC80💀
|
pB |
0xFFF000BD8
pointer to int
0x5C7BC80💀
|
Error Message : 💀는 데이터 경계에 할당되지 않았거나 잘못 정렬된 메모리를 가리키는 포인터를 의미합니다. 💀 위치는 근사하며 포인터의 실제 주소와 일치하지 않을 수 있습니다. 자세한 내용을 보려면 아래의 '바이트 수준 데이터 보기'를 선택하십시오:
pA = pB --> *pA = *pB 로 수

포인터가 가리키고 있는 메모리의 내용이 복사된다!
두번째 조심해야 할 점은 포인터가 '변수'일 때 문제가 되는 점입니다. 변하는 수를 가리키다 보니 가리키면 안 될 대상을 가리키는 논리적 오류가 발생할 수 있습니다.
포인터가 존재했을 때의 얕은 복사
#include <iostream>
using namespace std;
class CMyData
{
public:
CMyData(int nParam)
{
m_pnData = new int;
*m_pnData = nParam;
}
int GetData()
{
if (m_pnData != NULL)
return *m_pnData;
return 0;
}
private:
int* m_pnData = nullptr;
};
int main()
{
CMyData a(10);
CMyData b(a);
cout << a.GetData() << endl;
cout << b.GetData() << endl;
return 0;
}
위 코드에서 컴파일러는 아래의 얕은 복사를 자동으로 생성한다.
CMyData(const CMyData &rhs)
{
m_pnData = rhs.m_pnData;
}
위 코드에서 소멸자를 추가할 경우 아래와 같습니다.
#include <iostream>
using namespace std;
class CMyData {
public:
CMyData(int nParam) {
m_pnData = new int;
*m_pnData = nParam;
}
// 복사 생성자 추가
CMyData(const CMyData& rhs) {
m_pnData = new int;
m_pnData = rhs.m_pnData;
}
~CMyData() { // 소멸자 추가
if (m_pnData != nullptr) {
delete m_pnData;
m_pnData = nullptr;
}
}
int GetData() {
if (m_pnData != nullptr)
return *m_pnData;
return 0;
}
private:
int* m_pnData = nullptr;
};
int main() {
CMyData a(10);
CMyData b(a);
cout << a.GetData() << endl;
cout << b.GetData() << endl;
return 0;
}
이 경우에는 한 객체가 소멸되면 해당 메모리를 가리키던 포인터가 무효화됩니다. 그러나 다른 객체가 여전히 그 메모리를 가리키고 있기 때문에 소멸자가 그 메모리를 해제하는 작업은 무효화된 포인터를 조작하게 됩니다. 이것은 메모리 오염(memory corruption)을 유발할 수 있습니다.
이러한 경우를 예방하기 위한 깊은 복사를 수행해야 합니다. 깊은 복사를 수행할 경우 아래와 같습니다.
#include <iostream>
using namespace std;
class CMyData {
public:
CMyData(int nParam) {
m_pnData = new int;
*m_pnData = nParam;
}
// 복사 생성자를 깊은 복사로 수정
CMyData(const CMyData& rhs) {
m_pnData = new int;
*m_pnData = *rhs.m_pnData; // 깊은 복사(deep copy)
}
~CMyData() { // 소멸자 추가
if (m_pnData != nullptr) {
delete m_pnData;
m_pnData = nullptr;
}
}
int GetData() {
if (m_pnData != nullptr)
return *m_pnData;
return 0;
}
private:
int* m_pnData = nullptr;
};
int main() {
CMyData a(10);
CMyData b(a);
cout << a.GetData() << endl;
cout << b.GetData() << endl;
return 0;
}
'프로그래밍 > C++' 카테고리의 다른 글
Pass by value VS Pass by reference (0) | 2024.03.21 |
---|---|
대입 연산자 (0) | 2024.03.21 |
복사 생성자 (0) | 2024.03.20 |
Dynamic vs Static (0) | 2024.03.19 |
Valid struct operation (0) | 2024.03.19 |