c++가 객체지향 언어지만 기반이 c이기 때문에 객체를 전달할때 값에 의한 전달(pass by value)을 제공한다.
객체를 값에 의한 전달을 할 경우 다음 두가지 문제가 발생한다. (생각해보니 vector등의 객체들은 그냥 값으로 전달한 기억이 있다.)
1) 복사생성자, 복사소멸자 + 객체안에서 객체를 사용할 경우 그 객체의 복사생성자, 복사소멸자
class Temp{
public:
Temp(){
cout<<"Create Temp"<<endl;
}
~Temp(){
cout<<"Destroy Temp"<<endl;
}
};
class Base{
private:
Temp t;
public:
Base(){
cout<<"Create Base"<<endl;
}
~Base(){
cout<<"Destroy Base"<<endl;
}
virtual void show() const {
cout<<"show Base"<<endl;
}
};
void test(Base obj){
obj.show();
}
int main(void){
Base myBase;
cout<<"before call func"<<endl;
test(myBase);
cout<<"after call func"<<endl;
return 0;
}
실행결과
Create Temp
Create Base
before call func
show Base
Destroy Base
Destroy Temp
after call func
Destroy Base
Destroy Temp
앞서 말했던 것처럼 값에 의한 전달을 통해 객체를 전달할 경우 생성자 소멸자를 호출하는 비용이 만만치 않다.
2) 복사손실(slicing problem)
class Temp{
public:
Temp(){
cout<<"Create Temp"<<endl;
}
~Temp(){
cout<<"Destroy Temp"<<endl;
}
};
class Base{
private:
Temp t;
public:
Base(){
cout<<"Create Base"<<endl;
}
~Base(){
cout<<"Destroy Base"<<endl;
}
virtual void show() const {
cout<<"show Base"<<endl;
}
};
class Derived : public Base{
public:
Derived(){
}
void show() const {
cout<<"show Derived"<<endl;
}
};
void test(Base obj){
obj.show();
}
int main(void){
Derived myDerived;
cout<<"before call func"<<endl;
test(myDerived);
cout<<"after call func"<<endl;
return 0;
}
분명히 Base를 상속받아 show함수를 오버라이드 한 Derived객체를 전달했지만 결과는 예상치 못하게 나온다. 복사손실이 일어난 것이다.
이유는 Derived객체가 전달 될 때 Base객체로 복사생성자가 호출되면서 Derived객체의 정보들이 사라져버린다.
실행결과
Create Temp
Create Base
before call func
show Base <-----------문제발생!
Destroy Base
Destroy Temp
after call func
Destroy Base
Destroy Temp
한방의 해결책!
void test(const Base& obj){
obj.show();
}
실행결과
Create Temp
Create Base
before call func
show Derived
after call func
Destroy Base
Destroy Temp
간단한 수정으로 두가지 문제를 쉽게 해결했다. 객체를 전달할 때는 값에의한 전달을 지양해야 한다.
그러나 기본타입(int,double ...)과 STL의 iterator, 함수객체타입은 값에 의한 전달이 더 효율적으로 설계되었기 때문에 값에 의한 전달을 이용한다.
'C and C++' 카테고리의 다른 글
[C++] C++ 스타일의 캐스트 (0) | 2018.07.25 |
---|---|
[C++] 클래스를 캡슐화(Encapsulation) 하는 이유(왜 멤버변수는 대부분 private인가?) (1) | 2018.07.18 |
[C++] RAII스타일로 동작하는 lock_guard (0) | 2018.06.25 |
[C++] 자원관리 Smart Pointer (shared_ptr) (0) | 2018.06.17 |
[C++] 상속을 허용하는 Base 클래스의 소멸자는 반드시 가상(virtual) 소멸자로 선언 (0) | 2018.06.03 |