1. 개요
항목 20.에서 "값에 의한 전달"의 비효율성을 확인했고, 대체로 reference반환의 효율성이 좋아 객체반환을 reference로 했다. 하지만 포인터든 참조자든 결국 원본이 있어야 한다. 미리 결론을 말하자면,
객체를 참조자로 반환하면 안된다!
class Rational {
public :
Rational(int numerator = 0 , int denominator = 1);
…
private :
int n, d;
friend
const Rational& operator* (const Rational& lhs, const Rational& rhs);
//반환값이 const인 이유는… 3장 참조 (ex. i+j = 5)
};
* 풀이
- 참조자로 객체를 반환하는 oeprator * 함수이다.
- 위 함수 operator * 는 곱셈결과를 값으로 반환한다.
- 참조자로 Return하면 비용 부담은 줄일 수 있다. 하지만..
2. 객체의 참조자 반환
- 참조자를 반환하기 위해서는 실제 객체가 존재해야한다.
- 실제 객체가 존재할 수 있는 영역은 스택영역, 힙영역이다. 아래 코드를 통해 접근해보자.
(1) 스택영역
const Rational& operator* (const Rational& lhs, const Rational& rhs) {
Rational result(lhs.n * rhs.n, lhs.d * rhs.d);
return result;
}
* 풀이
- operator * 스코프안에서 정의된 지역변수 result를 볼 수 있다. 이를 참조값으로 반환하게 된다면 어떻게 될까?
- 스코프가 끝나면 값이 변하게 되므로, 참조값을 반환받은 쪽은 미정의 동작을 발생시킨다.
- 또한, 한번의 초기화 비용이 발생한다.
(2) 힙영역
const Rational& operator* (const Rational& lhs, const Rational& rhs) {
Rational *result = new Rational(lhs.n * rhs.n, lhs.d * rhs.d);
return *result;
}
* 풀이
- new를 통해 heap영역에 동적할당 해주었다.
- 여전히 생성자 호출 비용이 발생한다.
- delete는 누가 처리해줄까? --> 메모리 누수가 발생한다.
(3) 정적객체 반환
- 스레드 안전성문제 등..
3. 해결방법
inline const Rational operator* (const Rational& lhs, const Rational& rhs)
{
return Rational(lhs.n * rhs.n, lhs.d * rhs.d);
}
* 풀이
- 결론 : 새로운 객체를 반환하게 한다.
- 의문점 : 이 코드에서도 반환 값을 생성하고 소멸하지 않느냐?라고 생각할 수 있다.
- 답변 : C++의 컴파일러 개발자들은 가시적인 동작 변경을 가하지 않더라도 기존 코드의 수행성능을 높이는 최적화를 이미 심어두었다.. 이거쓰자..
요점정리
(1) 지역 스택 객체에 대한 포인터나 참조자를 반환하는 일, 혹은 힙에 할당된 객체에 대한 참조자를 반환하는 일, 또는 지역정적객체에 대한 포인터나 참조자를 반환하는 일은 그런 객체가 두개 이상 필요해질 가능성이 있다면 절대로 하면안된다.
(2) 지역정적객체에 대한 참조자 반환 설계 방법은 항목 4에 있다..
'책 정리 > Effective C++ 3rd' 카테고리의 다른 글
항목 23. 멤버함수 보다는 비멤버 비프렌드 함수와 더 가까워지자 (0) | 2020.01.09 |
---|---|
항목 22. 데이터 멤버가 선언될 곳은 private 영역임을 명심하자 (0) | 2020.01.09 |
항목 20. '값에 의한 전달'보다는 '상수객체 참조자에 의한 전달' 방식을 택하는 편이 대개 낫다 (0) | 2020.01.08 |
항목 19. 클래스 설계는 타입 설계와 똑같이 취급하자 (0) | 2020.01.07 |
항목 18. 인터페이스 설계는 제대로 쓰기엔 쉽게, 엉터리로 쓰기엔 어렵게 하자 (0) | 2020.01.07 |
댓글