[C++] 항목 20: ‘값에 의한 전달’보다는 ‘상수객체 참조자에 의한 전달’ 방식을 택하는 편이 대개 낫다
카테고리: Cpp
태그: Cpp
이 글은 아래의 책을 자세히 정리한 후, 정리한 글을 GPT에게 요약을 요청하여 작성되었습니다.
이펙티브 C++ 제3판, 스콧 마이어스 저자, 곽용재 번역
📦 4. 설계 및 선언
👉🏻 항목 20: ‘값에 의한 전달’보다는 ‘상수객체 참조자에 의한 전달’ 방식을 택하는 편이 대개 낫다
📌 객체의 전달
C++은 기본적으로 함수에 객체를 전달하거나 받을 때, ‘값에 의한 전달(pass-by-value)’ 방식을 사용한다.
따로 지정하지 않으면 아래처럼 동작한다:
- 객체를 전달할 때 → 매개변수는 실제 인자의 복사본으로 초기화된다.
- 객체를 전달받을 때 → 함수가 반환한 복사본을 돌려받는다.
이 복사 과정에서는 복사 생성자가 호출되고, 이게 꽤 비쌀 수 있다.
class Person {
public:
Person();
virtual ~Person();
...
private:
string name;
string address;
};
class Student : public Person {
public:
Student();
~Student();
...
private:
string schoolName;
string schoolAddress;
};
bool validateStudent_Val(Student s);
bool validateStudent_Ref(const Student& s);
int main() {
Student plato;
bool platoIsOK = validateStudent_Val(plato);
bool platoIsOK = validateStudent_Ref(plato);
}
1. validateStudent_Val(plato);
호출 시
- 복사 생성자 호출:
- Student 복사 생성자
- Person 복사 생성자
- Person 내 string name/address 복사 생성자
-
Student 내 string schoolName/schoolAddress 복사 생성자
→ 총 6번의 생성자 호출
- 함수 종료 시 소멸자 호출:
- Student 소멸자
- Student 내 schoolName/schoolAddress 소멸자
- Person 내 string name/address 소멸자
-
Person 소멸자
→ 총 6번의 소멸자 호출
→ 결론: 동작은 맞지만 비효율적이다.
2. validateStudent_Ref(plato);
호출 시
const
참조로 전달되기 때문에 복사 생성자가 호출되지 않는다.
실제로는 포인터를 넘기는 것과 거의 동일하게 작동.
게다가 const
덕분에 함수 내부에서 객체가 수정되는 것도 막을 수 있다.
✅ 참조에 의한 전달 방식의 장점
- 복사 자체가 생략되므로 비용이 적다.
- 복사손실(slicing) 을 막을 수 있다.
예를 들어 보자:
class Window {
public:
...
string name() const;
virtual void display() const;
};
class WindowWithScrollBars : public Window {
public:
...
virtual void display() const;
};
void printNameAndDisplay_Val(Window w) {
cout << w.name();
w.display();
}
void printNameAndDisplay_Ref(const Window& w) {
cout << w.name();
w.display();
}
int main() {
WindowWithScrollBars wwsb;
printNameAndDisplay_Val(wwsb);
printNameAndDisplay_Ref(wwsb);
}
1. printNameAndDisplay_Val(wwsb);
Window w = wwsb;
이런 식으로 기본 클래스(Window) 객체에 복사되므로,
파생 클래스(WindowWithScrollBars)의 기능은 모두 사라진다.
→ w.display()
는 기본 클래스 버전이 호출됨.
2. printNameAndDisplay_Ref(wwsb);
참조로 전달되기 때문에 원래 타입을 유지하며,
→ w.display()
는 파생 클래스의 버전이 호출된다.
📌 참조자 전달을 선호하는 이유
- 기본제공 타입은 값으로 전달해도 부담이 없다. (ex.
int
,double
) - 하지만 사용자 정의 타입은 내부적으로 무거운 복사를 유발할 수 있다.
- 심지어
double
하나로 만든 클래스라도 컴파일러가 레지스터에 안 얹어줄 수도 있다. - 사용자 정의 타입의 구조는 언제든지 변할 수 있다.
- 그러니 값에 의한 전달이 적절한 경우는 기본제공 타입, STL 반복자, 함수 객체 타입 정도다.
🧐 정리
-
대부분의 경우, 값에 의한 전달보다 상수 참조자 전달이 효율적이다.
게다가 복사손실(slicing) 문제도 예방할 수 있다. -
기본제공 타입, STL 반복자, 함수 객체 타입에 대해서만 값에 의한 전달이 더 낫다.
나머지는 참조자에 의한 전달을 사용하자.
댓글남기기