본문 바로가기

Effective C++/4. 설계 및 선언

항목 20: '값에 의한 전달'보다는 '상수 객체 참조자에 의한 전달' 방식을 택하는 편이 대개 낫다.

기본적으로 C++ : call by value => 함수 매개변수는 실제 인자의 copy 통해 초기화되며, 어떤 함수를 호출한 쪽은 함수가 반환한 값의 copy 돌려받는다.

 

copy => 복사 생성자로 만듬. 때문에 call by value == high cost

 

high cost? 만약 Person이라는 base class 상속받는 Student라는 dervied class 있다고 생각했을 ,

 

bool validateStduent(Student s); 

 

이런 함수가 있다고 치자. 여기에

 

Student plato;

bool platoIsStudent = validateStudent(plato);

 

여기서 매개변수 s 초기화 하기 위해 s(plato) 이런식으로 Student 복사 생성자가 호출된다. 그리고 함수가 끝나면 매개변수가 소멸되니 Student 소멸자가 호출된다.

근데 여기서 Student 복사 생성자가 호출될 , 데이터 맴버들의 생성자도 각각 호출되어야 한다. 그리고 Student 객체의 생성자가 호출되기 전에 Person 생성자도 호출해야 한다. 그리고 Person 데이터 맴버들의 생성자들도 각각 호출되어야 한다. 그리고 이는 소멸자를 호출할 때도 대응된다.

 

그에 반해 상수객체에 대한 참조자로 호출하는 방법은?(call by cosnt reference)

 

훨씬 효율적인 코드로 바뀜

 

일단 참조자로 넘김 => call by pointer 동작 방식 같음. => 사본같은거 전혀 만들 필요 없음. => 생성자, 소멸자 둘다 호출 안됨. 그리고 call by reference 객체 내용이 변할 수도 있기 때문에 const 키워드를 붙여주자..

그리고 이런 방식은 call by value에서 나타나는 복사손실 문제(slicing problem) 해결해준다. 만약 파생 클래스 객체가 기본 클래스 객체로서 전될되는 경우, 객체가 값으로 전달될 경우 기본 클래스의 복사 생성자가 호출된다.(컴파일러는 기본적으로 타입만 보고 결정한다, 이렇게 안될려면 virtual 키워드를 활용해야됨)

=> 이렇게 되면 derived class 부분은 아예 싹둑 잘린채 base class 매개변수에 복사되고, 함수는 매개변수를 활용할 것이므로 derived class 부분은 아예 쓰질 못한다.

 

 

그리고 call by reference => call by pointer 유사함. -> 기본제공 타입의 경우 call by value 효율이 좋을 있다. 그리고 STL 반복자(iterator), 함수 객체도 call by value 유리한데, 이는 예전부터 반복자와 함수 객체는 값으로 전달되록 설계해 왔기 때문이다. 참고로 반복자와 함수 객체를 구현할

  1. 복사 효율을 높일
  2. 복사손실 문제에 노출되지

둘을 지키면서 구현하기 때문에 call by value 써도 괜찮은 것이다.

 

 

기본제공 타입은 크기가 작다 -> 크기만 작으면 call by value 써도 된다? -> X

만약 데이터 맴버가 달랑 포인터 하나라고 해도 포인터가 가르키는 객체까지 복사해야하는 작업이 필요함(shallow copy 허용하지 않는 객체의 경우)

그럼 만약 객체의 크기가 작고, 복사 생성자도 그다지 비싸지 않게 만들어졌다고 가정하자. 그래도 안됨

=> 수행성능에 문제가 있을 있다.

컴파일러 중에는 기본제공 타입과 사용자정의 타입을 아예 다르게 취급하는 것들이 있다.

예를 들면 진짜 double 레지스터에 넣어주지만, double 하나로만 만들어진 객체는(하부 구조가 완벽히 똑같더라도) 레지스터에 넣지 않을 있다.

이런 개발환경에서 일하는 사람들은 call by reference 낫다. 포인터(참조자가 포인터로 구현됨)만큼은 레지스터에 확실히 들어가기 때문이다.

 

  • call by value보다 call by const-reference 선호하자. 대체적으로 효율적이고 복사손실 문제를 방지한다.
  • 기본제공 타입, iterator, 함수 객체 타입에는 call by value 적절하다.