복사생성자가 호출되지 않는 이유가 뭔가요?
글쓴이: dltkddyd / 작성시간: 금, 2014/05/23 - 12:38오후
Time 클래스에 AddTime 멤버함수가 다음과 같이 선언돼 있습니다.
const Time AddTime(const Time& T) const;
그리고 본문 코드에 다음과 같이 진술문은 언급했습니다.
Time C=A.AddTime(B);
여기서 C에 반환값이 대입되는 순간에 복사생성자가 호출돼야할 것으로 생각했는데, 복사생성자가 호출되지 않습니다. 물론 복사생성자는 클래스에 정의해놨고요. 복사생성자가 호출돼야하는거 아닌가요?
부연하자면 AddTime이 반환하는 값은 Time형의 지역객체입니다.
Forums:
RVO 또는 NRVO를 검색해보세요.
RVO 또는 NRVO를 검색해보세요.
rvo에 대한 개념이 그러니까..
rvo라는 것은 반환되는 함수의 반환객체에 대한 소멸 책임은 호출원에 있다는 것이죠? 저는 이렇게 이해했습니다. 그런데 선언과 동시에 +연산자를 호출할 때와 선언 후에 +연산자를 호출해서 바로 대입연산을 할 때 조금 어긋나는 것 같습니다.
1)에서는 복사생성자가 호출되지 않습니다.
그런데
2)에서는 복사생성자가 호출됩니다.
반환되는 지역객체를 선언과 동시에 대입받을 때와 반환되는 지역객체를 대입연산자를 통해 대입받을 때에 복사생성자 호출여부가 다른데요. 안 되려면 둘 다 복사생성자가 호출되어서는 안되죠?
이렇게 이해해야 할까요?
선언과 동시에 함수의 객체를 반환받을 때에는 그 소멸의 책임을 선언변수가 떠않는다. 그러나 그렇지 않을 경우에는 호출원의 임시객체가 생성되어 그 임시객체가 소멸의 책임을 떠않는다.
즉 '문제는 임시객체가 생성되느냐 안되느냐의 문제이다.'라고 이해하면 될까요? 틀린 부분이 있다면 어디가 틀렸을까요?
원래는 복사대입연산자 저런 식으로 반환을 언급하지 않지만 비교상 저렇게 언급해본겁니다.
본인 맞습니다.
인증샷
우헤헤헤... 로 대신합니다.
지금 포인트를 완전히 잘못 잡으셨습니다. 소멸
지금 포인트를 완전히 잘못 잡으셨습니다. 소멸 책임이고 뭐고는 이 문제와 전혀 상관이 없습니다.
질문글들 보면 열의도 있으시고 열심히 하시려는 것도 같아보이는데, 모래바닥 위에 모래성을 쌓으시는 것 같아보입니다.
lowlevel 쪽에 관심이 있으시다면 진심으로 '컴퓨터 구조 개론' 정도의 키워드로 원론적인 내용을 다시 한번 공부하셨으면 좋겠습니다.
간단하게 설명하자면 '객체를 생성한 후, 함수 내부에서 생성하여 리턴한 객체를 복사'하는 과정이 필요한데, 이러면 불필요한 데이터 복사 과정이 생기기 때문에 컴파일러가 이를 최적화해주는 것입니다.
1은 복사 생성자가 호출되는 것이고, 2는 대입 연산자가 호출되는 것입니다. 원래의 질문과는 전혀 상관 없는 주제입니다.
2에서도 복사생성자가 호출되는군요. 단 그
2에서도 복사생성자가 호출됩니다. 단 그 복사생성자는 +연산자가 아니라 대입연산자=이 값을 반환하는 과정에서 발생하는 것을 확인했습니다. 그러나까 호출원쪽에 해당되는 객체가 다시 반환될 때에는 복사생성자가 호출되는군요.
1에 대해서는 지금 검색해서 찾아보고 있는 중입니다.
본인 맞습니다.
인증샷
우헤헤헤... 로 대신합니다.
gilgil.net
Test obj3=obj1+obj2; //1)
이 부분은
Test obj3(obj1+obj2); //1)
와 동일합니다.
www.gilgil.net
obj1+obj2 에서 반환된 지역객체가 다시
obj1+obj2 에서 반환된 지역객체가 다시 복사생성자로 전달이 될테고, 그러면 복사생성자의 출력문이 출력되어야 합니다. 그런데 출력문이 출력되지 않습니다. 해당 출력문이 출력되지 않는다는 것은 복사생성자가 호출되지 않은 것으로 보이는데요. 쿠캬캬님은 복사생성자가 호출되었다고 하시기에 어째서 그런가를 지금 탐구하고 있습니다.
본인 맞습니다.
인증샷
우헤헤헤... 로 대신합니다.
오호~ 그렇군요.
최적화가 없다면
Test obj3;
obj3=obj1+obj2; // 이건 assign operator
Test obj3=obj1+obj2; // 이건 copy constructor
가 호출되어 진다고 보는 게 맞습니다.
그런데 RVO 정책에 의해서 1)번 코드는
1. obj1+obj2아 이루어 지고(+ operator)
2. 그 결과가 obj3에 들어 가게 되는데(copy constructor)
디버깅을 해 보면 + operator의 temp 객체의 포인터와 obj3의 포인터 값이 같습니다.
즉, 이는 RVO가 적용되었다는 뜻이고, 당연히 copy constructor가 호출되지 않을 수 있겠네요.
제 생각에는 컴파일러마다 다를 거 같습니다(확실하지는 않음).
http://en.wikipedia.org/wiki/Return_value_optimization
www.gilgil.net
생성자를 호출하지 않고도 정상적인 객체를 만들어내는 것이
그러니까 아래에 링크 걸어주신 글을 해석해봤습니다. 답변 글로 달려고 했는데 아래로 글이 올라가버리네요. 그러니가 RVO라는 것이 생성자를 호출하지 않고도 정상적으로 객체가 형성되도록 하는 건가보네요. 생성자를 호출하는 것이 물론 C++의 표준 사항이겠으나 컴파일러의 최적화를 위해서 생성자 호출을 하지 않고 구현된 내부의(전 그게 무엇인지 모르지만) 더 빠른 방식을 선택해서 컴파일하는 기법. 이런 식으로 받아들이면 될까요?
RVO 자체가 C++표준의 구현이군요.
생성자를 호출하는 것이 정석이지만 컴파일러 최적화를 위해 함수가 지역객체 반환시 또는 선언시 함수의 반환값을 대입받을 시에 생성자를 호출하지 않고 객체를 생성하는 구현 방식을 RVO라고 한다라고 이해했습니다. 올바르게 이해한 것인지 모르겠네요.
네, 그렇게 보시면 될 것 같습니다.
저도 어설프게 알고 있었는데 이번 기회에 확실히 알게 되었네요.
감사합니다. ^^
www.gilgil.net
감사드려야 할 사람은 저인것 같습니다.
제가 감사드립니다. 좋은 링크 걸어주셔서 모르는 것을 알게됐습니다.
본인 맞습니다.
인증샷
우헤헤헤... 로 대신합니다.
아, 제가 글을 잘못 올렸습니다.
저 부분을 고치면 포인터가 이상작동하는 경우가 발생하지 않습니다. 죄송합니다. 제가 생성자를 잘못 작성했네요.
그리고 링크 걸어주신 내용 해석해봤습니다.
일반적으로 C++ 표준은 컴파일러가 어떠한 최적화라도 수행하도록 한다. 그리고 마치 표준요구사항들이 이행된 양 결과적으로 실행가능한 전시성의 준수할 만한 동작들을 제공한다. 이를 통상 "as-if rule"이라고 한다. RVO는 C++ 표준에서 as-if rule보다 진보된 특별한 구문을 언급한다. 비록 복사생성자가 부수적인 효과를 수반한다할지라도, 어떤 구현은 반환진술문으로부터 초래되는 복사연산을 제거할지도 모른다.
다음 예제는 비록 복사 생성자가 가시적인 부수 효과를 수반한다 할지라도 그 구현이 하나 또는 두 개의 복제물들을 제거할지도 모르는 시나리오를 강조한다. 제거될지도 모르는 첫 번째 복제물은 C()가 함수 f의 반환 값으로 복사되는 것이다. 두 번째 복제물은 f에 의해 반환된 임시 객체의 복제물이 obj에 이르는 것이다.
컴파일러와 컴파일러의 설정에 따라서 그 프로그램의 결과는 다음 출력의 어떠한 것이라도 출력하것이다.
Hello World!
A copy was made.
A copy was made.
Hello Wordl!
A copy was made.
Hello World!
댓글 달기