목차
C++ 복사 생성자
복사 생성자는 객체의 복사가 이루어질 때 호출되는 생성자이다.
아래와 같은 클래스가 있다고 해보자.
class Rectangle {
private:
int width;
int height;
public:
Rectangle(); // 기본 생성자
Rectangle(const Rectangle& src); // 복사 생성자
Rectangle& operator=(const Rectangle& src); // 복사 대입 연산자
~Rectangle(); // 소멸자
}
C++
복사
그리고 복사 생성자는 보통 아래와 같이 복사 대입 연산자를 이용해서 구현한다.
// 복사 생성자
Rectangle::Rectangle(const Rectangle& src) {
*this = rhs; // 복사 대입 연산자 호출
}
// 복사 대입 연산자
Rectangle& Rectangle::operator=(const Rectangle& rhs) {
if (this != &rhs) {
this->width = rhs.width;
this->height = rhs.height;
}
return (*this);
}
C++
복사
하지만 왜 복사 생성자의 매개변수는 꼭 레퍼런스(&) 자료형으로 받아야 하는 걸까?
왜 아래와 같이 값으로 받으면 안되는 걸까?
// 매개변수를 레퍼런스가 아닌 값으로 받음
Rectangle(const Rectangle src) {
*this = rhs; // 복사 대입 연산자 호출
}
C++
복사
복사 생성자의 매개변수를 값으로 받으면 안되는 이유
결론부터 말하면 복사 생성자의 매개변수를 값으로 받으면 무한 반복에 빠지기 때문이다.
아래와 같이 코드가 있다고 해보자.
// copy.cpp
class Rectangle {
private:
int width;
int height;
public:
Rectangle(int width, int height);
Rectangle(const Rectangle src);
};
Rectangle::Rectangle(int width, int height) {
this->width = width;
this->height = height;
}
Rectangle::Rectangle(const Rectangle src) {
*this = src;
}
int main() {
Rectangle r1(10, 20);
Rectangle r2(r1);
return 0;
}
C++
복사
MacOS 환경에서 c++ 컴파일러로 컴파일을 시도하면 아래와 같은 에러가 표시된다.
copy.cpp:7:29: error: copy constructor must pass its first argument by reference
Rectangle(const Rectangle src);
^
const &
copy.cpp:15:38: error: copy constructor must pass its first argument by reference
Rectangle::Rectangle(const Rectangle src) {
^
const &
2 errors generated.
C++
복사
즉, 컴파일러 차원에서도 복사 생성자는 반드시 레퍼런스로 넘겨야 한다고 말하고 있다.
그렇다면, 값으로 매개변수로 넘겨주었을 때 어떤 과정을 거치길래 무한 반복에 빠지는 것일까?
코드와 함께 정리해보면 아래와 같다.
1.
새로운 객체가 복사 생성자를 한다.
Rectangle r2(r1);
C++
복사
2.
복사 생성자에서는 매개변수로 자료형이 레퍼런스가 아닌 값으로 온 것을 확인한다.
Rectangle::Rectangle(const Rectangle src) {
*this = src;
}
C++
복사
3.
값으로 전달된 매개변수는 스택 영역에 복사해야 한다. 따라서 Rectangle 클래스를 복사하기 위해 Rectangle 클래스의 복사 생성자를 다시 호출한다.
// 2번의 복사 생성자에서 호출한 복사 생성자
Rectangle::Rectangle(const Rectangle src) {
*this = src;
}
C++
복사
4.
위의 과정이 반복해서 일어나기 때문에 무한 반복에 빠진다.
여기서 하나 더 검증해야 할 것이 있다.
매개변수로 값이 들어왔을 때 복사 생성자가 호출되는 지 확인해야 한다.
아래와 같이 코드를 작성했다.
// copy.cpp
#include <iostream>
class Rectangle {
private:
int width;
int height;
public:
Rectangle(int width, int height);
Rectangle(const Rectangle& src);
Rectangle& operator=(const Rectangle& rhs);
void print(const Rectangle obj);
};
Rectangle::Rectangle(int width, int height) {
this->width = width;
this->height = height;
}
Rectangle::Rectangle(const Rectangle& src) {
std::cout << "Copy Constructor" << std::endl;
*this = src;
}
Rectangle& Rectangle::operator=(const Rectangle& rhs) {
if (this != &rhs) {
std::cout << "Copy Assignment Operator" << std::endl;
this->width = rhs.width;
this->height = rhs.height;
}
return (*this);
}
void Rectangle::print(const Rectangle obj) {
std::cout << "Width: " << obj.width << std::endl;
std::cout << "Height: " << obj.height << std::endl;
}
int main() {
Rectangle r1(10, 20);
r1.print(r1);
return 0;
}
C++
복사
c++ 컴파일러로 컴파일 후 출력한 결과는 아래와 같다.
Copy Constructor
Copy Assignment Operator
Width: 10
Height: 20
Plain Text
복사
따라서 복사 생성자의 매개변수는 반드시 값이 아닌 레퍼런스로 받아야 한다는 결론을 내릴 수 있다.