c++ / 6. 복사 생성사란. (+ 대입 연산자 오버로딩)
(Copy constructor, substitution operator)
(글 마지막 부분에서 고쳐지게 되어 미리 공지합니다.
글 초반에 나오는 복사 생성자라는 함수는 여러 종류로 나타날 수 있으며 보통의 경우는 맨 마지막 코드와 같이 표현이 됩니다.)
필자는 복사 생성자를 간과하고 있었습니다. 그러다가 복사 생성자의 무서움(?)을 얼마전에 알게되어서 책을 보고 다시 공부를 했습니다.
복사 생성자란 생성되는 객체에 이미 생성된 객체의 값을 복사하는 역활을 수행합니다.
이 부분이 필요한 것은 값이 할당되어 있는 클래스 변수간의 할당에 사용되고,
응용으로는 call by value와 call by reference의 차이를 이해하는 것이 있습니다.
당장은 뜬금없는 소리같지만 글을 쓰면서 무슨말인지 풀어 보겠습니다.
우선 복사 생성자의 예제를 작성해보겠습니다.
****** show *****
height : 180
weight : 66.5
****** show *****
height : 180
weight : 66.5
이런 상황에서 생성자에 변수 하나를 없애보겠습니다.
그 결과 아래와 같은 에러를 만날 수 있었습니다.
hklee@hklee:~/Dropbox/cpp/copyConstructor$ g++ main.cpp -o out
main.cpp:22:1: error: prototype for ‘CopyConstructor::CopyConstructor(double)’ does not match any in class ‘CopyConstructor’
CopyConstructor::CopyConstructor(const double weight) {
^
main.cpp:4:7: error: candidates are: CopyConstructor::CopyConstructor(const CopyConstructor&)
class CopyConstructor {
^
main.cpp:7:5: error: CopyConstructor::CopyConstructor(double, double)
CopyConstructor(const double m_height, const double m_weight);
^
마지막으로 평소에 헷갈리던 실험을 하나만 해봤습니다.
간단히 생각하면 copy constructor이라면 할당을 의미하는 ' = '도 작동을 하는가입니다.
그래서 코드를 간단히 적었습니다.
그랬더니 결과가 에러로 나왔습니다.
main.cpp: In function ‘int main()’:
main.cpp:34:21: error: no matching function for call to ‘CopyConstructor::CopyConstructor()’
CopyConstructor person3;
^
main.cpp:34:21: note: candidates are:
main.cpp:15:1: note: CopyConstructor::CopyConstructor(double, double)
CopyConstructor::CopyConstructor(const double height, const double weight) {
^
main.cpp:15:1: note: candidate expects 2 arguments, 0 provided
main.cpp:4:7: note: CopyConstructor::CopyConstructor(const CopyConstructor&)
class CopyConstructor {
^
main.cpp:4:7: note: candidate expects 1 argument, 0 provided
당연한 이야기일지 모르지만 연산자 오버로딩을 하지 않은 결과로 보입니다.
그러면 연산자 오버로딩을 한다면 적용이 될까? 라는 궁금증이 생겼습니다.
그래서 작성해봤습니다.
대입연산자 오버로딩.
대입연산자 오버로딩을 하기 전에 초기 값을 설정하지 않는 객체를 만들기 위해서 빈 생성자를 만들고
대입 연산자 오버로딩을 하는 함수를 적어 봤습니다.
그 결과는 아래와 같이 당연히 동일한 값이 나왔습니다.
****** show *****
height : 180
weight : 66.5
****** show *****
height : 180
weight : 66.5
****** show *****
height : 180
weight : 66.5
여기까지가 복사 생성자가 무엇인지였고, 응용 부분에선 어떻게 사용되는지 생각해보겠습니다.
예제를 하나 준비했습니다. 아래 코드를 보시면 ex1과 ex2가 있습니다.
두 함수의 차이는 수령인자가 reference type이냐, 일반 변수냐가 다르지만 내부 로직은 너무 큰 차이가 존재합니다.
바로 일반 변수를 사용하게 되면 새로운 변수를 만들면서 카피 컨스트럭터(copy constructor)가 불리게 되고,
reference type으로 값을 받게 되면 약간의 포인터만 전달하게 되어 Copy constructor가 불리지 않게 됩니다.
한번 Copy constructor에 프린트문을 넣어서 출력해보겠습니다.
아... Copy Constructor라고 생각한 constructor는 두경우 모두 한번만 불리게 되네요...
흠... 수정하겠습니다.
Copy constructor는 불리지 않지만 메모리 측면에서 많은 부분을 차지하게 됩니다.
소스를 잘못 짠듯 하네요...
소스의 구현을 헤더에도 하고 외부에도 해서 우선순위의 문제가 생긴듯 합니다.
그래서 소스를 정리하고 다시 시도해보겠습니다.
(어쨋든 위의 코드로 실제 프로젝트에서 사용했다면 어마무시한 멘붕에 빠질뻔했네요...)
이것 저것 해봤는데 복사 생성자는 불리지 않았습니다.
좀더 구글링을 통해서 검색해볼 예정이지만 아직은 call by reference나 call by value나 모두 복사생성자가 불리지 않는다는 것이 결론입니다.
(2014. 9. 23)
(다른 의견이 있으시다면 댓글에 남겨주세요^^)
감사합니다.
(2014. 9. 23)
구글링을 통해서 call by value의 경우 copy constructor가 불리는 케이스를 찾았습니다.
근데 제가 봐왔던 책과는 다른 방법으로 copy constructor function을 구현했는데, 생각해보면 구글에서 본 형태가 맞는것 같습니다.
(책의 경우도 정상적으로 동작하니 틀린건 아니겠죠^^)
아래의 코드에서 ex1과 ex2를 둘 중 하나씩만 주석처리를 하면서 살펴보면 Copy constructor 의 동작을 확인할 수 있습니다.
ex1을 on한후 결과(주석처리 하지 않은 경우)
init HERE
Copy constructor CALL
After Function call..
Copy constructor CALL
ex2을 on한후 결과(주석처리 하지 않은 경우)
init HERE
Copy constructor CALL
After Function call..
따라서 call by value을 할 경우 Copy constructor가 불리게 되는 것을 확인하였습니다.
이 현상이 애를 먹일 경우는 refCounter를 사용할 때, 대입에 대해 ref가 1 올라가게 설정이 되어 있는데 단순 함수 호출에도 대입이 발생하여
ref count의 수가 오차가 발생하게 되는 경우가 존재합니다.
이번건 애좀 먹었네요;;;
감사합니다
c++ / 6. 복사 생성사란. (+ 대입 연산자 오버로딩) 끝.