reference:
"명품 C++ Programming" / 황기태
"전문가를 위한 C++" / 마크 그레고리
함수에 객체를 전달할 때(call-by-value) 컴파일러가 해당 객체의 복제 생성자를 호출하는 방식으로 초기화한다. 예를 들어 아래와 같은 코드를 보자.
void printString(string str) {
cout << str << endl;
}
string은 일종의 클래스이다. 따라서 위 메서드에 string 타입의 인수를 전달하면 string 매개변수인 str은 이 클래스의 복제 생성자를 호출하는 방식으로 초기화된다.
string name = "hellow world!";
printString(name);
위 코드가 실행되면, 메서드의 매개변수였던 str은 삭제된다. 이 값은 name 변수의 복사본이였기에 name은 그대로 남아있다.
함수에서 객체를 값으로 반환할 때도 복제 생성자가 호출된다.
(이에 참조 반환이 따로 있는 것이다.)
복제 생성자를 직접 작성하지 않으면 컴파일러가 대신 만들어준다. 컴파일러가 만든 복제 생성자는 데이터 멤버가 기본 타입이라면 똑같이 복사하고, 객체 타입이라면 그 객체의 복제 생성자를 호출한다.
복제 생성자는 아래와 같이 작성할 수 있다.
class Person {
public:
..
Person(const Person& src);
..
};
복제 생성자는 원본 객체에 대한 const 레퍼런스를 인수로 받는다. 이 복제 생성자 안에서 원본 객체에 있는 데이터 멤버 모두 복사한다.
만약 데이터 멤버에 포인터가 있어 얕은 복사가 이루어지려 한다면, 이 복제 생성자를 직접 작성하여 깊은 복사를 유도할 수 있다.
class MyClass {
private:
int member_num;
public:
// 기본 생성자
MyClass() {
member_num = 0;
cout << "기본 생성자 호출" << endl;
}
// 복사 생성자
MyClass(const MyClass& c) {
member_num = c.member_num;
cout << "복사 생성자 호출" << endl;
}
// 복사 대입 연산자
MyClass& operator=(const MyClass& c) {
member_num = c.member_num;
cout << "복사 대입 연산자 호출" << endl;
return *this;
}
int getter() {
return member_num;
}
};
void printMemberData(MyClass c) {
cout << c.getter() << endl;
}
int main() {
MyClass myClass; // 기본 생성자 호출
MyClass myClass2(myClass); // 복사 생성자 호출
MyClass myClass3 = myClass; // 복사 생성자 호출 (선언과 동시에 초기화)
MyClass myClass4; // 기본 생성자 호출
myClass4 = myClass; // 복사 대입 연산자 호출 (선언 이후 대입)
printMemberData(myClass); // 복사 생성자 호출
}