[Effective C++] #20 값에 의한 전달, 참조자에 의한 전달
Scott Meyers의 "Effective C++"를 통해, C++ 구현에 필요한 개념들을 이해하고, 기록하기 위함입니다. 해당 항목은 4장 "설계 및 선언", 항목 21 "함수에서 객체를 반환해야 할 경우에 참조자를 반환하려고 들지 말자"에 해당하는 내용입니다.
참조자를 반환하는 함수
class Rational
{
public:
Rational(const int numerator, const int denominator)
:n(numerator), d(denominator){}
private:
int n, d;
friend
const Rational operator*(const Rational& lhs, const Rational& rhs);
};
const Rational& operator*(const Rational& lhs, const Rational& rhs)
{
Rational result(lhs.n * rhs.n, lhs.d * rhs.d);
return result;
}
참조자를 반환하는 함수는 유의해야 할 작성 방식입니다. 위 예제 코드는 유리 수간의 곱셈 연산을 수행하는 연산자 함수(opeator*)를 보여줍니다. 이때, 이 프렌드 연산자 함수는 Rational 객체의 참조자를 반환하고 있습니다. 알다시피 "result" 객체는 지역 객체이므로, 함수의 종료와 함께 소멸됩니다. 따라서, 이후 과정에서 result의 참조자는 빈 메모리 공간을 가리키게 됩니다. 아래 예제는 reuslt 객체의 메모리를 동적 할당받는 경우입니다.
동적 할당받은 객체의 참조자를 반환하는 함수
class Rational
{
public:
Rational(const int numerator, const int denominator)
:n(numerator), d(denominator){}
private:
int n, d;
friend
const Rational operator*(const Rational& lhs, const Rational& rhs);
};
const Rational& operator*(const Rational& lhs, const Rational& rhs)
{
Rational *result = new Rational(lhs.n * rhs.n, lhs.d * rhs.d);
return *result;
}
"new" 키워드를 통해 동적 할당 받은 reuslt 객체의 메모리 공간은 "delete"로 해제해 주어야 합니다. 하지만, 참조자를 반환하는 함수는 참조자를 반환하고 끝나버립니다. 자원 누출의 원인이 되겠죠.
정적 객체의 참조자를 반환하는 함수
const Rational& operator*(const Rational& lhs, const Rational& rhs)
{
static Rational result = (lhs.n * rhs.n, lhs.d * rhs.d);
return result;
}
Rational a, b, c, d;
if((a*b) == (c*d))
{
어떤 처리...
}else
{
어떤 처리...
}
그렇다면, 정적 객체의 참조자를 반환하는 경우도 생각해보죠. 만약 reuslt를 정적 객체로 선언한 뒤 이에 대한 참조자를 반환한다면, 위 if 조건문은 어떠한 경우에도 true의 결과를 내겠죠. 지역 변수는 자동 주기를 가집니다. 쉽게 말하자면, 정의되는 시점에 생성되며, 소속되어 있는 블록이 끝나는 지점에서 소멸됩니다. static 키워드를 사용한 정적 변수는 정적 주기를 가집니다. 이 또한 쉽게 말하자면, 생성된 범위가 종료되어도 해당 값을 유지하는 변수이며, 프로그램 수명 내내 지속됩니다. 당연하게도 * 연산의 결과는 매번 같은 값을 가지며, 비교문의 의미가 없어지죠.
지역 스택 객체, 힙에 할당된 객체, 지역 정적 객체 등에 대한 포인터 혹은 참조자를 반환하는 일은 최대한 피해야 합니다.
'언어 > Effective C++' 카테고리의 다른 글
[Effective C++] #23 비 멤버, 비 프렌드 함수 (0) | 2022.01.28 |
---|---|
[Effective C++] #22 클래스 데이터 멤버, 접근 제어, 접근 지정자 (0) | 2022.01.28 |
[Effective C++] #20 값에 의한 전달, 참조자에 의한 전달 (0) | 2022.01.24 |
[Effective C++] #19 클래스 설계 (0) | 2022.01.24 |
[Effective C++] #18 인터페이스 설계 (0) | 2022.01.18 |