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);
  1. validateStudent의 파라미터로 s를 만들기 위해 복사생성자가 호출된다.
  2. validateStudent 함수를 리턴하면서 소멸자가 호출된다.
  3. 복사생성자가 호출될 때, 즉 객체를 새로 만들 때 멤버변수들에 대한 생성자도 동시에 생성된다. 즉 std::string의 생성자가 2번 호출된다.
  4. 소멸자가 호출될 때 마찬가지로 std::string의 소멸자도 2번 호출된다.
  5. Student가 상속을 받았기 때문에 Person의 생성자오 Person의 멤버변수에 대한 생성자도 호출된다.
  6. 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. 값에 의한 전달을 해도 상관 없는 타입은?
  1. 기본제공타입
  2. STL의 반복자
  3. 함수 객체

예전부터 반복자와 함수객체는 값으로 전달되도록 설계해 왔다. 이들을 구현할 때는 반드시 복사 효출을 높이고 복사손실 문제에 노출되지 않도록 만들어야 한다.

6. 새로만든 타입의 크기가 작다고 무조건 값으로 전달하면 안되는 이유는?
  • 딥카피를 해야 하는 경우
  • 컴파일러에 따라 기본제공타입과 같은 크기의 클래스라도 레지스터에 넣지 않는 경우도 있다
  • 사용자 정의 타입은 언제라도 수정해서 크기가 커질 수 있다.

정리

  • 값에 의한 전달보다는 상수객체 참조자에 의한 전달을 선호하자. 대체적으로 효출적이며 복사손실을 방지해 준다.
  • 기본제공타입 및 STL 반복자, 함수객체 타입은 값에 의한 전달을 하는 것이 좋다.

results matching ""

    No results matching ""