bdfgdfg

[C++] 깊은 복사(Deep Copy) vs 얕은 복사(Shallow Copy) 본문

게임프로그래밍/C++

[C++] 깊은 복사(Deep Copy) vs 얕은 복사(Shallow Copy)

marmelo12 2022. 1. 1. 20:13
반응형

디폴트 기본 복사 생성자 및 복사 대입 연산자를 사용할시에 얕은 복사로 복사가 이루어짐.

 

깊은 복사와 얕은 복사라는 말이 나온 이유는 클래스의 복사 생성,대입 연산자의 호출에 있어서

메모리 관점에 대한 주의.

간단하게 말하면 얕은 복사는 디폴트 복사 생성자,대입 연사자를 호출하여 복사 대상의 멤버를 복사할 객체의

멤버로 그대로 복사하는 것.

깊은 복사는 사용자가 복사 생성자,대입 연산자를 직접 정의하여 포인터 변수가 가르키는 메모리는 복사하지 않고 새로운 인스턴스를 만들어 복사하는 방식.

 

참고로 복사 생성자 및 복사 대입 연산자의 호출방식은

 - 생성시기 때 같은 타입을 인자로 넣어준다면 복사 생성자를,

 - 생성시기 때 같은 타입을 대입 연산한다면 복사 생성자를,

 - 그 이후 일반 상황에서 대입 연산한다면 복사 대입 연산자

 

얕은 복사는 단순히 같은 클래스의 객체 A,B가 존재하고 A의 멤버를 B의 멤버로 그대로 복사한다라고 했다.

이게 왜? 싶지만 문제가 발생하는 경우를 보자.

#include <iostream>
using namespace std;

class Pet
{
public:
	int	m_hp = 100;
};

class Knight
{
	
public:
	Pet* m_pet;
};
int main(void)
{

	Knight* k1 = new Knight();
	k1->m_pet = new Pet();
	
    
	delete k1->m_pet;	
	Knight* k2 = new Knight(*k1);
	k2->m_pet->m_hp = 50;

	std::cout << k2->m_pet->m_hp << std::endl;
	delete k1;
	delete k2;

	return 0;
}

위와 같은 경우다. 기본적으로 클래스는 생성자를 직접 만들지 않더라도 컴파일러가 만들어준다.

위 예제에서 Knight 클래스의 객체 k1을 생성하고 멤버 Pet 객체도 할당해주었다.

여기서 새로 생성한 k2객체를 생성하면서 k1객체를 토대로 복사 생성자를 호출한다.

그런 다음 k1 객체의 멤버 pet 객체를 delete후 pet 객체에 접근해보면

이렇게 크래시가 터진다.

delete를 한 객체를 대상으로 메모리를 역참조하여 값을 수정했으므로.

 

위는 매우 간단한 예제이다. 코드가 방대해지면 이러한 얕은 복사를 진행 했을 때 위와 같은 문제가 발생할 수 있다.

 -> 더 무서운것은 크래시가 터지지 않는 상황.

 

그렇기에 확실한 이유가 있는게 아닌이상 클래스 내부 멤버에 포인터 멤버를 들고 있을 시 깊은 복사를 해주어야 한다.

class Pet
{
public:
	int	m_hp = 100;
};

class Knight
{
public:
	Knight() : m_pet(nullptr) {}
	Knight(const Knight& k)
	{
		this->m_pet = new Pet(*k.m_pet);
	}
public:
	Pet* m_pet;
};
int main(void)
{

	Knight* k1 = new Knight();
	k1->m_pet = new Pet();
	
	delete k1->m_pet;
	
	Knight* k2 = new Knight(*k1);
	k2->m_pet->m_hp = 50;

	std::cout << k2->m_pet->m_hp << std::endl;
	delete k1;
	delete k2;

	return 0;
}

즉 위와 같이 복사가 진행될 때 클래스의 멤버가 포인터인 멤버변수는 새로운 메모리로 동적 할당 후 복사가 진행 되어야 한다.

 -> 만약 Pet클래스의 멤버도 포인터변수가 있었다면 깊은 복사를 진행 해줘야한다.

 

 

 

 

 

 

반응형
Comments