Item 20: '값에 의한 전달'보다는 '상수객체 참조자에 의한 전달' 방식을 택하는 편이 낫다
1. 아래 코드에서 validateStudent() 함수를 호출하면 생성자/소멸자 호출은 어떻게 되는가?
class Person {
public:
Person();
virtual ~Person();
...
private:
std::string name;
std::string address;
};
class Student: public Person {
public:
Student();
~Student();
...
private:
std::string schoolName;
std::string schoolAddress;
};
bool validateStudent(Student s);
Student plato;
bool platoIsOk = validateStudent(plato);
- validateStudent의 파라미터로 s를 만들기 위해 복사생성자가 호출된다.
- validateStudent 함수를 리턴하면서 소멸자가 호출된다.
- 복사생성자가 호출될 때, 즉 객체를 새로 만들 때 멤버변수들에 대한 생성자도 동시에 생성된다. 즉 std::string의 생성자가 2번 호출된다.
- 소멸자가 호출될 때 마찬가지로 std::string의 소멸자도 2번 호출된다.
- Student가 상속을 받았기 때문에 Person의 생성자오 Person의 멤버변수에 대한 생성자도 호출된다.
- Student 객체가 소멸될 때 마찬가지로 기본 클래스의 소멸자 및 기본 클래스의 멤버변수의 소멸자가 호출된다.
단순히 클래스 객체 하나를 값으로 전달했을 뿐인데 생성자가 총 6번, 소멸자도 총 6번 호출이 되었다.
2. 해결 방법은?
bool validateStudent(const Student& s);
상수객체에 대한 참조자로 전달하게 되면 객체가 새로 생성될 필요가 없어서 생성자 및 소멸자가 호출될 일이 없다.
3. 상수객체의 참조에 의한 전달 방식의 장점은?
- 전달한 객체가 변경될 걱정을 할 필요가 없다.
- 복사손실 문제(Slicing Problem)를 방지할 수 있다.
class Window {
public:
...
std::string name() const;
virtual void display() const;
};
class WindowWithScrollBars: public Window {
public:
...
virtual void display() const;
};
void printNameAndDisplay(Window w)
{
std::cout << w.name();
w.display();
}
위 함수를 아래와 같이 사용하면 복사손실이 생긴다.
WindowWithScrollBars wwsb;
printNameAndDisplay(wwsb);
printNameAndDisplay() 함수가 호출되면 Window의 생성자만 호출되면서 wwsb가 WindowWithScrollBars 객체의 구실을 할 수 있는 정보가 모두 사라지게된다. 또한 함수안에서 WindowWithScrollBars::display 함수가 호출되지 않고 Window::display 함수만 호출된다.
4. 위 복사손실 문제의 해결법은?
void printNameAndDisplay(Window& w)
{
std::cout << w.name();
w.display();
}
5. 값에 의한 전달을 해도 상관 없는 타입은?
- 기본제공타입
- STL의 반복자
- 함수 객체
예전부터 반복자와 함수객체는 값으로 전달되도록 설계해 왔다. 이들을 구현할 때는 반드시 복사 효출을 높이고 복사손실 문제에 노출되지 않도록 만들어야 한다.
6. 새로만든 타입의 크기가 작다고 무조건 값으로 전달하면 안되는 이유는?
- 딥카피를 해야 하는 경우
- 컴파일러에 따라 기본제공타입과 같은 크기의 클래스라도 레지스터에 넣지 않는 경우도 있다
- 사용자 정의 타입은 언제라도 수정해서 크기가 커질 수 있다.
정리
- 값에 의한 전달보다는 상수객체 참조자에 의한 전달을 선호하자. 대체적으로 효출적이며 복사손실을 방지해 준다.
- 기본제공타입 및 STL 반복자, 함수객체 타입은 값에 의한 전달을 하는 것이 좋다.