C++에서는 함수로부터 객체를 전달받거나 함수에 객체를 전달할 때 '값에 의한 전달' 방식을 사용합니다.
함수 매개변수는 실제 인자의 사본을 통해 초기화되며, 어떤 함수를 호출한 쪽은 그함수가 반환한 값의 사본을 돌려 받습니다.
위의 내용처럼 사본을 만들어내는 원천이 바로 복사 생성자 입니다.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 | 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 platoIsValid = ValidateStudent( plato ); |
plato로부터 매개변수 s를 초기화시키기 위해 Student의 복사 생성자가 호출된다. s는 ValidateStudent가 복귀할 때 소멸된다. Student 객체가 생성될 때마다 string 멤버도 생성된다. 게다가 Student 객체는 Person 객체로부터 파생되었기 때문에, Student 객체의 생성자가 호출되기 이전에 Person 객체가 생성된다. Person 객체가 생성될 때에도 string 멤버도 생성된다.
bool ValidateStudent( const Student& s ); //참조의 의한 전달 방식으로 바꾸기만 하면 됩니다.
이렇게 할 경우,
우선 새로 만들어지는 객체라는 것이 없으므로 생성자/소멸자가 호출되지 않는다.
그리고 const를 붙임으로써 전달인자로 보낸 객체가 함수에 의해 변하지 않는다는 것을 명시해준다. 원래 값에 의한 전달 방식때도 원본 객체는 보호되는데 그 의미를 그대로 적용한 것이라고 보면 됩니다.
또한, '복사 손실' 문제도 없어집니다. 아래의 코드를 봅시다.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 | class Window { public : std::string GetName() const; virtual void Display() const; }; class WindowWithScrollBars : public Window { public : .... virtual void Display() const; }; void PrintNameAndDisplay( Window w ) { std::cout << w.GetName(); w.Display(); } WindowWithScrollBars wwsb; PrintNameAndDisplay( wwsb ); |
참조자는 보통 포인터를 써서 구현됩니다. 즉, 참조자를 전달한다는 것은 결국 포인터를 전달한다는 의미인데,
전달하는 객체 타입이 기본제공 타입(int, char 등) 이라면 참조자보다는 값에 의한 전달을 하는 쪽이 더 효율적일 때가 많다.
기본제공 타입은 대개 크기가 작은 편이다.
그러나! 타입 크기가 작다고 해서 전부 값에 의한 전달이 효율적이지는 않다.
한개의 포인터 멤버만 가지고 있는 객체가 있다면(STL 컨테이너가 대표적) 이 객체를 복사할 때 포인터가 가리키는 대상까지 복사하는 작업도 따라다녀야 해서 오히려 비용이 더 들 수 있게 된다.
객체 크기도 작고 복사 생성자도 간단하게 만들어졌다 해도, 일부 컴파일러에서 기본제공 타입과 사용자 정의 타입을 다르게 취급할 수 있기 때문에(레지스터에 들어가는지 안들어가는지의 차이가 존재한다) 수행 성능에 있어서 차이를 보일 수 있다.
또한 사용자 정의 타입은 크기 변화에 언제든 노출이 되어있다. 지속적으로 추가 될 수 있는 부분이 있다는 말입니다.
이것 만은 잊지 말자!
◆ '값의 의한 전달' 보다는 '상수 객체 참조자에 의한 전달'을 선호 합시다. 대체적으로 효율적일 뿐만 아니라 복사 손실 문제까지 막아 줍니다.
'0x0001 > Effective C++' 카테고리의 다른 글
[Effective C++] 항목 19 : 클래스 설계는 타입 설계와 똑같이 취급하자 (0) | 2019.02.13 |
---|---|
[Effective C++] 항목 18 : 인터페이스 설계는 제대로 쓰기엔 쉽게, 엉터리로 쓰기엔 어렵게 하자 (0) | 2019.02.13 |
[Effective C++] 항목 17 : new로 생성한 객체를 스마트 포인터에 저장하는 코드는 별도의 한 문장으로 만들자 (0) | 2019.02.13 |
[Effective C++] 항목 16 : new 및 delete를 사용할 때는 형태를 반드시 맞추자 (0) | 2019.02.13 |
[Effective C++] 항목 15 : 자원 관리 클래스에서 관리되는 자원은 외부에서 접근할 수 있도록 하자 (0) | 2019.02.12 |