Unreal TIL 6일차 - C++ Copy Constructor

yys·2026년 3월 4일

TIL

목록 보기
6/86
post-thumbnail

복사 생성자


간단히 말해, 한 객체를 복사하여 다른 객체로 붙이는 생성자이다.

복사 생성자가 호출되는 경우는 3가지로 분류된다.

  • 객체 복사(Pass by Value)
void DisplayPlayer(Player p)
{
	p.Print(); // p는 player의 사본
}

int main()
{
	Player player {0, 0, 1};
	DisplayPlayer(player);
}
  • Value의 형태로 결과값 반환
Player CreatePlayer()
{
	Player player{1, 1, 1};
	return player; // 객체가 복사되어 전달
}

Player player;
player = CreatePlayer();
  • 기존 객체를 기반으로 새로운 객체 생성
Player player {1, 1, 1};

Player anotherPlayer = player; // 생성과 동시에 복사 생성자 호출
/*
다음은 복사 생성자가 호출 x
Player anotherPlayer;
anotherPlayer = player;
*/

복사 생성자의 기본 구조는 다음과 같다.

  • 참조자(&)가 있는 건 불필요한 복사 과정을 없애기 위함이다.
    • 만약 &가 없다면 메모리 공간을 더 사용하게 된다.
  • '복사'에만 초점을 두기 때문에 값을 변경할 일이 없으므로 const도 포함이 된다.
Player::Player(const Player& source);

복사 생성자는 기본적으로 구현하지 않으면 얕은 복사(Shallow Copy)가 수행된다.

Player(const Player& source)
	: x{source.x}, y{source.y}
{
	// ...
}

기본적으로 문제가 없어보이지만, 아래 코드에서 문제점을 발견할 수 있다.

class Shallow
{
private:
	int* data;
public:
	Shallow(int d) // 생성자
	{
		data = new int;
		*data = d;
	}
	~Shallow() // 소멸자
	{
		delete data;
	}
	Shallow(const Shallow& source) // 복사 생성자
		:data{source.data}
	{
	}
int main() // 주석으로 작성한 주소는 임의로 작성한 것으로, 실제 주소가 아님
{
	Shallow obj1{100}; // 새로운 메모리 할당. obj1.data의 주소는 1000
	{
		Shallow obj2 = obj1; // 복사 생성자에 의한 얕은 복사 수행. obj2.data도 1000을 가리킴
	} // obj2에 대한 소멸자 수행. 주소 1000에 할당된 메모리 해제
	return 0;
} // obj1에 대한 소멸자 수행 -> 이미 해제한 메모리를 또 다시 해제하려고 함 -> 에러

위 코드를 보면 obj1과 obj2가 같은 주소를 가지고 있어서, 하나의 메모리 공간을 가지게 된다.

  • 즉, 얕은 복사는 값만 복사하므로 객체가 포인터를 멤버 변수를 가지고 있다면 이중 해제(double-free) 현상을 일으킬 수 있다.

아래는 이해하기 쉽게 그려보았다.

그래서 이를 방지하기 위해 깊은 복사(Deep Copy)가 나왔고, '값을 복사하고 새로운 힙 공간을 할당한 뒤 그곳에 값을 넣는다.'가 주 내용이다.

  • 생성자 위임은 기존에 만들어진 생성자를 활용하는 기법이다.
  • 깊은 복사를 위해서는 직접 복사 생성자를 정의해줘야 한다.
class Deep
{
private:
	int* data;
public:
	Deep(int d) // 생성자
	{
		data = new int;
		*data = d;
	}
	~Deep() // 소멸자
	{
		delete data;
	}
	Deep(const Deep& source) // 복사 생성자
		:Deep{*source.data} // 생성자 위임
	{
	}
int main() // 주소는 임의로 작성한 것으로, 실제 주소가 아님
{
	Deep obj1{100}; // 새로운 메모리 할당. obj1.data의 주소는 1000
	{
		Deep obj2 = obj1; // 복사 생성자에 의한 깊은 복사 수행. obj2.data는 주소 1004를 가리킴
	} // obj2에 대한 소멸자 수행. 주소 1004에 할당된 메모리 해제
	return 0;
} // obj1에 대한 소멸자 수행 -> 주소 1004에 할당된 메모리 해제

다시 그림을 그리면 다음과 같다.

C++을 배우면 무조건 거치는 중요한 내용이긴 하지만, 자주 활용할 기회가 없었기 때문에 자꾸 까먹는 부분이 아쉬울 따름이다...

참고 자료

profile
게임 개발 지망생

0개의 댓글