[문과 코린이의 IT 기록장] C,C++ - 복사생성자( 복사생성자란?, 얕은 복사 vs 깊은 복사 )
1. 복사생성자란?
1) 복사생성자 형태
: 클래스이름 (const 클래스 이름 &참조변수){ }
ex) SoSimple(const SoSimple & copy){ }
2) 복사생성자로 객체 생성 방법
a. 클래스명 생성할 객체 = 이미 생성된 객체;
b. 클래스명 생성할 객체(이미 생성된 객체); // 이 방법을 사용하는 것이 더 좋음.
// 어처피 전자는 암묵적인 형변환이 일어나기 때문에, explict로 선언된다면 형변환이 불가능하기 때문
3) 기본 복사 생성자
- 기본적으로 제공되는 생성자 중 하나.
- 객체간의 변수의 값을 복사해준다.
- 호출시키는 객체의 선언과 동시에 초기화할 때 생성한다.
ex )
2. 얕은 복사 vs 깊은 복사
1) 얕은 복사
- 값만 복사하는 복사 형태 ( 맴버 대 맴버의 복사 )
- 포인터 등을 복사할 시 주소를 참조하기 때문에, 값 변경, 동적 해제 등에서 문제가 발생한다.
* 메모리 누설 발생 , 한 대상의 값을 변경하면 다른 대상의 값 또한 바뀌어 버리는 등의 문제 발생 가능
ex ) 얕은 복사의 문제점
- 메모리 누설 사례
: delete[]name으로, man2의 소멸자가 먼저 소멸되면, man1이 참조하던 문자열이 이미 소멸됐으므로, man2의 소멸자는 실행될 수 없다.
이 문제를 해결하기 위해, 복사생성자를 활용할 때는, 깊은 복사를 사용해야 한다.
2) 깊은 복사
- 주소가 가리키는 값, 즉 그 메모리의 값까지 복사하는 복사 형태
- 포인터 등을 복사 시 한 주소를 참조하지 않고, 새로 주소를 할당받은 후 참조하기 때문에, 문제가 발생하지 않는다.
[ 복사생성자의 호출 시점 ] (1) 기존에 생성된 객체를 이용해서, 새로운 객체를 초기화하는 경우 ex ) SoSimple obj2 = obj1; // obj1도 SoSimple객체라 가정 (2) Call-By-Value방식의 함수호출 과정에서, 객체를 인자로 전달하는 경우 ex ) SoSimple SimpleFuncObj(SoSimple ob){ ... } .... int main(){ SoSimple obj; SoSimpleFuncobj(obj); } (3) 객체를 반환하되, 참조형으로 반환하지 않는 경우 ex ) SoSimple SimpleFuncObj(SoSimple ob){ ... return ob; } ... int main(){ SoSimple obj; SoSimpleFuncobj(obj); } |
(1) 기존에 생성된 객체를 이용해서, 새로운 객체를 초기화하는 경우
- 앞의 깊은 복사 예시를, 깊은 복사의 형태로 바꿔 작성해보자.
ex )
[ 복사생성자의 구현 부분 ]
Person(const Person & copy) : age(copy.age){
name = new char[strlen(copy.name) + 1];
strcpy(name, copy.name); // 문자열 주소 복사.
}
(2) Call-By-Value방식의 함수호출 과정에서, 객체를 인자로 전달하는 경우
[ 복사생성자의 구현 부분 ]
SoSimple(const SoSimple & copy) : num(copy.num){ // 복사생성자
cout<<"Called SoSimple(const SoSimple & copy)" << endl;
}
...
Void SimpleFuncObj(SoSimple ob){
// ob객체가 인자로 전달된다. 따라서, SoSimple ob = obj와 마찬가지이므로, obj객체가 ob객체라는 이름으로 복사된다. 이때 복사생성자로 오버로딩되므로, 위의 구문이 실행된다.
ob.ShowData();
}
(3) 객체를 반환하되, 참조형으로 반환하지 않는 경우
[ 복사생성자의 구현 부분 ]
Class SoSimple{
...
SoSimple(const SoSimple & copy) : num(copy.num){ // 복사생성자
cout<<"Called SoSimple(const SoSimple & copy)" << endl;
}
...
};
SoSimple SimpleFuncObj(SoSimple ob){
// 매개변수 선언을 보면, 인자의 생성과정에서 복사생성자가 호출됨을 알 수 있다. (ob 객체 생성)
cout << "return 이전" << endl;
return ob;
// ob객체 반환. 반환형이 참조형이 아니므로, ob의 복사본이 만들어지면서 반환이 진행됨.
// 즉, 임시객체의 복사생성자가 호출되면서, ob의 내용이 전달되고, ob는 사라진다.
}
Int main(){
SoSimple obj(7);
SimpleFuncObj(obj).AddNum(30).ShowData();
// SimpleFuncobj함수가 반환된 객체를 대상으로, AddNum함수가 호출되고, 이어서 AddNum함수가 반환하는 참조값(*this)을 대상으로, ShowData함수를 호출하고 있다.
* 유의사항 - 아직 공부하고 있는 문과생 코린이가, 정리해서 남겨놓은 정리 및 필기노트입니다. - 정확하지 않거나, 틀린 점이 있을 수 있으니, 유의해서 봐주시면 감사하겠습니다. - 혹시 잘못된 점을 발견하셨다면, 댓글로 친절하게 남겨주시면 감사하겠습니다 :) |