Effective C++ 3판 중 20번째 항목을 보면 '값에 의한 전달' 보다는 '상수 객체 참조자에 의한 전달'을 권장하고 있다.

일단 새로 만든 자료형(클래스나 구조체)을 값에 의한 전달(pass-by-value)을 하게되면 당연히 다수의 생성자와 소멸자 호출이 있기 때문에 효율이 낮아진다는 것이다. 때문에 덩치가 큰 객체(클래스의 인스턴스)는 참조에 의한 전달(pass-by-reference)를 하는 것이 좋다는 것인데, 이는 C++ 을 어느정도 공부한 사람이라면 당연한 이야기로 여길 것이다.

상수 객체에 대한 참조자(reference-to-const)란 무엇일까? 이는 바로 다음과 같은 코드를 의미한다.


class Person { ... };
class Student : public Person { ... };

bool validateStudent(const Student& s);



위와 같이 상수 객체 참조자에 의한 전달을 하여야 효율적이라는 것인데, 여기에는 '복사 손실 문제(slicing problem)' 문제까지도 해결하고자 하는 의도가 숨겨져있다. 복사 손실 문제 또는 슬라이스 문제란 파생 클래스 객체가 기본 클래스 객체로서 전달되는 경우, 이 객체가 값으로 전달되면 기본 클래스의 복사 생성자가 호출되고, 파생 클래스 객체로 동작하게 하는 특징들이 사라져버리는 문제를 일컫는다. 복사 손실 문제를 해결한, 상수 객체 참조자를 이용하는, 다음의 프로그램 코드를 살펴보자.

#include <iostream>

using namespace std;

class AA
{
public:
    int a;

    virtual void pp() const { cout << "aa" << endl; }
};

class BB : public AA
{
public:
	int b;

	virtual void pp() const { cout << "bb" << endl; }
};

void func(const AA& aa)
{
	cout << "a = " << aa.a << endl;
	// cout << "b = " << aa.b << endl; // 에러 발생
	aa.pp();
}

int main(void)
{
	BB bb;
	bb.a = 1;
	bb.b = 2;
	bb.pp();

	func(bb);
}


위 프로그램을 컴파일하고 실행시키면 결과는 어떨까?? main 함수에서 자식 클래스 BB의 객체를 생성하고, func 함수는 부모 클래스의 상수 참조형으로 받는다. 그리고, 공통으로 존재하는 함수 pp 를 호출하는 것인데... 그 결과는 다음과 같다.

bb
a = 1
bb

만약 클래스 AA 와 BB 의 멤버 함수 pp() 에 virtual 지시자가 없으면 어떻게 될까? 이 경우 컴파일 에러는 발생하지 않는다. 다만, 함수 func 에서의 출력 결과는 조금 다르게 나타난다.

bb
a = 1
aa

이는 virtual 지시자의 원래 역할을 제대로 수행하고 있음을 의미한다. 사실 이러한 virtual 지시자에 대한 예제는 보통 객체를 포인터 타입으로 넘겨줄 때를 예제로 들어서 설명하는 것이 많은데, 실제 참조형으로 넘겨줄 때에도 이러한 점이 통한다는 것이 신기했다. 이런게 되나 싶던 것이 된다는 것이랄까?? 암튼, virtual 지시자의 역할에 대해서는 C++ 문법책을 참조하기 바란다.

암튼, Effective C++ 책은 잘못이해하기 쉬운 내용들을 콕! 찝어서 설명해주는 아주 영양가 만점의 책이다. 특히나 곽용재씨가 번역한 3판은 어찌나 번역이 아름다운지(단순히 번역을 잘했다가 아니라 아름다운거다), 2판 번역서를 보면서 답답했던 맘을 아주 잘 누그려뜨려주어서 맘에 든다.


0


Posted by kkokkal
: