[C++] 얕은 복사 vs 깊은 복사 1
얕은 복사 vs 깊은 복사 1
인프런 Rookiss님의 'Part1: C++ 프로그래밍 입문' 강의를 기반으로 정리한 필기입니다.
😎[C++과 언리얼로 만드는 MMORPG 게임 개발 시리즈] Part1: C++ 프로그래밍 입문 강의 들으러 가기!
목차
기본
#include <iostream>
using namespace std;
class Pet {
public:
Pet() { cout << "Pet()" << endl; }
~Pet() { cout << "~Pet()" << endl; }
Pet(const Pet& pet) { cout << "Pet(const Pet& pet)" << endl; }
};
class Knight{
public:
int _hp = 100;
Pet* _pet;
};
int main()
{
Pet* pet = new Pet();
Knight knight; // 기본 생성자
knight._hp = 200;
knight._pet = pet;
Knight knight2 = knight; // 복사 생성자
Knight knight2(knight); // 복사 생성자. 위와 동일. 표기방법 차이
Knight knight3; // 기본 생성자
knight3 = knight; // 복사 대입 연산자
return 0;
}
Knight 클래스에 Pet 클래스를 들고 있다.
[복사 생성자] + [복사 대입 연산자]
둘 다 안 만들어주면 컴파일러가 '암시적으로' 만들어준다
중간 결론) 컴파일러가 알아서 잘 만들어준다?
수고하세요~ 다음 주제 넘어갈까요? << NO
얕은 복사 Shallow Copy
#include <iostream>
using namespace std;
class Pet {
public:
Pet() { cout << "Pet()" << endl; }
~Pet() { cout << "~Pet()" << endl; }
Pet(const Pet& pet) { cout << "Pet(const Pet& pet)" << endl; }
};
class Knight{
public:
int _hp = 100;
Pet* _pet;
};
int main()
{
Pet* pet = new Pet();
Knight knight; // 기본 생성자
knight._hp = 200;
knight._pet = pet;
Knight knight2 = knight; // 복사 생성자
Knight knight2(knight); // 복사 생성자. 위와 동일. 표기방법 차이
Knight knight3; // 기본 생성자
knight3 = knight; // 복사 대입 연산자
// knight1, knight2, knight3 모두 같은 pet을 공유하고 있다.
return 0;
}
멤버 데이터를 비트열 단위로 '똑같이' 복사 (메모리 영역 값을 그대로 복사)
포인터는 주소값 바구니 -> 주소값을 똑같이 복사 -> 동일한 객체를 가리키는 상태가 됨
Stack : Knight1 [ hp 0x1000 ] -> Heap 0x1000 Pet[ ]
Stack : Knight2 [ hp 0x1000 ]
Stack : Knight3 [ hp 0x1000 ]
knight1, knight2, knight3 모두 같은 pet을 공유하고 있다.
_pet 메모리의 주소가 같다
※ 얕은 복사가 문제가 되는 상황
Knight 안에 생성자와 소멸자가 추가된 상황
#include <iostream>
using namespace std;
class Pet {
public:
Pet() { cout << "Pet()" << endl; }
~Pet() { cout << "~Pet()" << endl; }
Pet(const Pet& pet) { cout << "Pet(const Pet& pet)" << endl; }
};
class Knight{
public:
Knight() { _pet = new Pet(); }
~Knight() { delete _pet; }
public:
int _hp = 100;
Pet* _pet;
};
int main()
{
Pet* pet = new Pet();
Knight knight; // 기본 생성자
knight._hp = 200;
knight._pet = pet;
Knight knight2 = knight; // 복사 생성자
Knight knight2(knight); // 복사 생성자. 위와 동일. 표기방법 차이
Knight knight3; // 기본 생성자
knight3 = knight; // 복사 대입 연산자
return 0;
}
컴파일 시 다음과 같은 화면이 뜨며 컴파일 되지 않는다.
Stack : Knight1 [ hp 0x1000 ] -> Heap 0x1000 Pet[ ]
Stack : Knight2 [ hp 0x1000 ]
Stack : Knight3 [ hp 0x1000 ]
Knight 1, 2, 3이 같은 Pet을 가리키고 있기 때문에 delete _pet이라는 코드가 3번 실행된다.
이런 경우, double free 문제가 발생한다. 여러번 삭제해서 문제가 되는 것이다.
깊은 복사 Deep Copy
#include <iostream>
using namespace std;
class Pet {
public:
Pet() { cout << "Pet()" << endl; }
~Pet() { cout << "~Pet()" << endl; }
Pet(const Pet& pet) { cout << "Pet(const Pet& pet)" << endl; }
};
class Knight{
public:
Knight() { _pet = new Pet(); }
Knight(const Knight& knight){ // 명시적 복사 생성자
_hp = knight._hp;
_pet = new Pet(*(knight._pet)); // 깊은 복사
}
Knight& operator=(const Knight& knight){ // 명시적 복사 대입 연산자
_hp = knight._hp;
_pet = new Pet(*(knight._pet)); // 깊은 복사
return *this;
}
~Knight() { delete _pet; }
public:
int _hp = 100;
Pet* _pet;
};
int main()
{
Pet* pet = new Pet();
Knight knight; // 기본 생성자
knight._hp = 200;
knight._pet = pet;
Knight knight2 = knight; // 복사 생성자
Knight knight2(knight); // 복사 생성자. 위와 동일. 표기방법 차이
Knight knight3; // 기본 생성자
knight3 = knight; // 복사 대입 연산자
return 0;
}
' 복사 생성자 '와 ' 복사 대입 연산자 '를 명시적으로 만들어준다!
멤버 데이터가 참조(주소) 값이라면, 데이터를 새로 만들어준다 (원본 객체가 참조하는 대상까지 새로 만들어서 복사)
포인터는 주소값 바구니 -> 새로운 객체를 생성 -> 상이한 객체 가리키는 상태가 됨
Stack : Knight1 [ hp 0x1000 ] -> Heap 0x1000 Pet[ ]
Stack : Knight2 [ hp 0x2000 ] -> Heap 0x2000 Pet[ ]
Stack : Knight3 [ hp 0x3000 ] -> Heap 0x3000 Pet[ ]
_pet 메모리의 주소가 다르다.
전체코드
#include <iostream>
using namespace std;
// 얕은 복사 vs 깊은 복사
class Pet
{
public:
Pet()
{
cout << "Pet()" << endl;
}
~Pet()
{
cout << "~Pet()" << endl;
}
Pet(const Pet& pet)
{
cout << "Pet(const Pet& pet)" << endl;
}
};
class RabbitPet : public Pet
{
};
class Knight
{
public:
Knight()
{
_pet = new Pet();
}
Knight(const Knight& knight)
{
_hp = knight._hp;
_pet = new Pet(*(knight._pet)); // 깊은 복사
}
Knight& operator=(const Knight& knight)
{
_hp = knight._hp;
_pet = new Pet(*(knight._pet)); // 깊은 복사
return *this;
}
~Knight()
{
delete _pet;
}
public:
int _hp = 100;
Pet* _pet;
};
int main()
{
Pet* pet = new Pet();
Knight knight; // 기본 생성자
knight._hp = 200;
knight._pet = pet;
Knight knight2 = knight; // 복사 생성자
//Knight knight3(knight);
Knight knight3; // 기본 생성자
knight3 = knight; // 복사 대입 연산자
// [복사 생성자] + [복사 대입 연산자]
// 둘 다 안 만들어주면 컴파일러가 '암시적으로' 만들어준다
// 중간 결론) 컴파일러가 알아서 잘 만들어준다?
// 수고하세요~ 다음 주제 넘어갈까요? << NO
// [ 얕은 복사 Shallow Copy ]
// 멤버 데이터를 비트열 단위로 '똑같이' 복사 (메모리 영역 값을 그대로 복사)
// 포인터는 주소값 바구니 -> 주소값을 똑같이 복사 -> 동일한 객체를 가리키는 상태가 됨
// Stack : Knight1 [ hp 0x1000 ] -> Heap 0x1000 Pet[ ]
// Stack : Knight2 [ hp 0x1000 ]
// Stack : Knight3 [ hp 0x1000 ]
// [ 깊은 복사 Deep Copy ]
// 멤버 데이터가 참조(주소) 값이라면, 데이터를 새로 만들어준다 (원본 객체가 참조하는 대상까지 새로 만들어서 복사)
// 포인터는 주소값 바구니 -> 새로운 객체를 생성 -> 상이한 객체 가리키는 상태가 됨
// Stack : Knight1 [ hp 0x1000 ] -> Heap 0x1000 Pet[ ]
// Stack : Knight2 [ hp 0x2000 ] -> Heap 0x2000 Pet[ ]
// Stack : Knight3 [ hp 0x3000 ] -> Heap 0x3000 Pet[ ]
return 0;
}
'⭐ Programming > C++' 카테고리의 다른 글
[C++] 캐스팅: static, dynamic, const, reinterpret cast (0) | 2022.04.10 |
---|---|
[C++] 얕은 복사 vs 깊은 복사 2 (0) | 2022.04.10 |
[C++] 타입 변환 4: 포인터 (0) | 2022.04.09 |
[C++] 타입 변환 3: 포인터 타입 변환 (0) | 2022.04.08 |
[C++] 타입 변환 2: 참조 타입 변환 (0) | 2022.04.08 |
댓글
이 글 공유하기
다른 글
-
[C++] 캐스팅: static, dynamic, const, reinterpret cast
[C++] 캐스팅: static, dynamic, const, reinterpret cast
2022.04.10 -
[C++] 얕은 복사 vs 깊은 복사 2
[C++] 얕은 복사 vs 깊은 복사 2
2022.04.10 -
[C++] 타입 변환 4: 포인터
[C++] 타입 변환 4: 포인터
2022.04.09 -
[C++] 타입 변환 3: 포인터 타입 변환
[C++] 타입 변환 3: 포인터 타입 변환
2022.04.08