기본적으로 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가 유리한데, 이는 예전부터 반복자와 함수 객체는 값으로 전달되록 설계해 왔기 때문이다. 참고로 반복자와 함수 객체를 구현할 때
- 복사 효율을 높일 것
- 복사손실 문제에 노출되지 말 것
이 둘을 지키면서 구현하기 때문에 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가 더 적절하다.
'Effective C++ > 4. 설계 및 선언' 카테고리의 다른 글
항목 23: 함수보다는 비멤버 비프렌드 함수와 더 가까워지자 (0) | 2021.04.24 |
---|---|
항목 22: 데이터 맴버가 선언될 곳은 private 영역임을 명심하자. (0) | 2021.04.24 |
항목 21: 함에서 객체를 반환해야 할 경우에 참조자를 반환하려고 들지 말자. (0) | 2021.04.24 |
항목 19: 클래스 설계는 타입 설계와 똑같이 취급하자. (0) | 2021.04.24 |
항목 18: 인터페이스 설계는 제대로 쓰기엔 쉽게, 엉터리로 쓰기엔 어렵게 하자 (0) | 2021.04.24 |