728x90
반응형

값에 의한 전달 방식

기본적으로 C++은 함수로부터 객체를 전달받거나 함수에 객체를 전달할 때 값에 의한 전달(pass-by-value) 방식을 사용한다.

특별히 다른 방식을 지정하지 않은 함수 매개변수는 실제 인자의 사본을 통해 초기화되며, 어떤 함수를 호출한 쪽은 그 함수가 반환한 값의 사본을 돌려받는다.

이들 사본을 만들어 내는 것은 복사 생성자인데, 이 점 때문에 값에 의한 전달이 고비용의 연산이 되기도 한다.

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);

위의 예시에서 plato를 인자로 전달하면 매개 변수 s를 초기화하기 위해 Student의 복사 생성자를 호출하며 validateStudent함수를 나갈 때 소멸자도 호출될 것이다.

그리고 Person클래스를 상속받았기 때문에 Person의 생성자가 먼저 호출되고 Student의 생성자가 호출, 각 클래스의 필드 또한 복사될 것입니다.

결과적으로, 위의 예시 같은 경우 총 생성자 6번에 소멸자 6번이 호출되어 비용을 소모하고 있음을 알 수 있다.

 

상수 객체에 대한 참조자 전달 방식

값에 의한 전달 방식에서 말한 문제를 해결하려면, 상수 객체에 대한 참조자로 전달하면 된다.

bool validateStudent(const Student& s);

이렇게 참조자 전달 방식을 택하게 되면 아래의 장점이 있다.

1. 새로운 객체가 만들어지지 않으므로 생성자와 소멸자가 호출되지 않아 비용 소모가 없다.

2. const를 사용함으로써 전달되는 객체가 변경되지 않음을 보장한다.

3. 복사 손실 문제가 없어지는 장점이 있다.

 

앞의 1, 2번 같은 경우는 쉽게 이해될 수 있으나 3번의 경우가 무슨 말인지 모호할 수 있으니 자세히 알아보자.

복사 손실 문제란 보통 파생 클래스 객체가 기본 클래스 객체로서 전달되는 경우에 접할 수 있는데, 이때 이 객체가 값으로 전달되면 기본 클래스의 복사 생성자가 호출되고 파생 클래스 객체의 특징들은 잘려나가게 된다는 문제를 말한다.

아래의 예시를 보자.

class Window
{
public:
   std::string name() const;
   virtual void display() const;
};

class WindowWithScrollBars: public Window
{
public:
   virtual void display() const;
};

// 1번
void printNameAndDisplay(Window w)
{
   std::cout << w.name();
   w.display();
}

// 2번
void printNameAndDisplay(const Window& w)
{
   std::cout<< w.name();
   w.display();
}

WindowWithScrollBars wwsb;
printNameAndDisplay(wwsb);

printNameAndDisplay함수의 인자로 1번의 값에 의한 전달을 택하게 되면 Window의 복사 생성자를 호출하기 때문에 WindowWithScrollBars를 넘기더라도 Window의 display만을 호출하게 된다.

따라서 2번의 참조자로 전달 방식을 채택해야 하며, 이렇게 전달 시 어떤 종류의 Window가 넘겨지더라도 그 성질을 가지게 된다.

 

전달 방식 채택 시 고려해야 할 점

참조자는 보통 포인터를 써서 구현된다.

즉, 참조자를 전달한다는 것은 결국 포인터를 전달한다는 내용과 같다.

그러면 전달하는 객체의 타입이 기본 제공 타입(int 등) 일 경우에는 참조자로 넘기는 것보다 값으로 넘기는 편이 더 효율적이라는 것도 알 수 있는데, 이 점은 STL의 반복자와 함수 객체도 마찬가지다. 예전부터 반복자와 함수 객체는 값으로 전달되도록 설계해 왔기 때문이다.

 

결론적으로,

값에 의한 전달

  • 기본 자료형(int 등) 일 경우
  • STL의 반복자와 함수 객체의 경우
    • 반복자와 함수 객체 구현 시 고려할 점
      1. 복사 효율을 높여야 함
      2. 복사 손실 문제에 노출되지 않도록 해야 함

 

참조자 전달

  • 사용자 타입을 사용할 경우
  • 값에 의한 전달이 효율적인 경우를 제외한 모든 상황

 

요약

  • 값에 의한 전달보다는 상수 객체 참조자에 의한 전달을 선호하자. 대체적으로 효율적일 뿐만 아니라 복사 손실 문제까지 막아준다.
  • 이번 항목에서 다룬 법칙은 기본 제공 타입 및 STL 반복자, 그리고 함수 객체 타입에는 맞지 않는다. 이들에 대해서는 값에 의한 전달이 더 적절하다.
728x90
반응형