간단히 말해, 한 객체를 복사하여 다른 객체로 붙이는 생성자이다.
복사 생성자가 호출되는 경우는 3가지로 분류된다.
void DisplayPlayer(Player p)
{
p.Print(); // p는 player의 사본
}
int main()
{
Player player {0, 0, 1};
DisplayPlayer(player);
}
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;
*/
복사 생성자의 기본 구조는 다음과 같다.
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가 같은 주소를 가지고 있어서, 하나의 메모리 공간을 가지게 된다.
아래는 이해하기 쉽게 그려보았다.

그래서 이를 방지하기 위해 깊은 복사(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++을 배우면 무조건 거치는 중요한 내용이긴 하지만, 자주 활용할 기회가 없었기 때문에 자꾸 까먹는 부분이 아쉬울 따름이다...
참고 자료
- learncpp: Copy Constructor
https://www.learncpp.com/cpp-tutorial/introduction-to-the-copy-constructor/