복사 생성자
-> 생성자 : 객체 생성 시 객체를 받아온다.
-> 인자로 넘어오는 객체의 멤버 값을 복사 받는 것.
디폴트 복사 생성자
-> 사용자가 복사 생성자를 정의하지 않으면 컴파일러가 복사 생성자를 생성하여 호출한다.
-> 사용자가 복사 생성자를 정의하면 컴파일러는 복사 생성자를 생성하지 않는다.
복사 생성자의 호출 시점 3가지
#1. 먼저 생성한 객체를 나중에 생성하는 객체의 인자로 전달하는 경우
#2. 함수의 인자로 객체가 전달되는 경우
#3. 함수의 반환 값으로 객체가 반환되는 경우
복사 생성자의 인자로 레퍼런스 타입을 받는 이유
-> 생성자는 함수의 일종이므로 호출하면 stack 영역이 할당된다.
-> 생성자의 stack 영역에 지역 변수를 할당하고 인자로 넘겨주는 값을 복사 받을 때
-> 또 다시 복사 생성자를 호출해야 하는 문제가 발생한다.
-> 즉, 무한루프가 걸린다.
레퍼런스가 아닌 포인터로 받으면 어떻게 될까?
-> 다음과 같이 포인터 연산으로 인해서 문제(주소 값 변경)가 발생할 수도 있기 때문에 포인터로 받지 않는다.
int a = 10;
int* ptr = &a;
int* ptr2 = ptr;
ptr2 = ptr + 1;
디폴트 복사 생성자의 문제점
-> 얕은 복사의 사용
-> 얕은 복사는 단순 대입으로 인한 복사이다.
얕은복사
-> 얕은 복사는 객체가 가진 멤버들의 값을 새로운 객체로 복사하는데 만약 객체가 참조타입의 멤버를 가지고 있다면 참조값만 복사가 된다.
class CObj
{
public:
CObj() : m_pBuff(nullptr) {}
CObj(char* _pBuff)
{
m_pBuff = new char[strlen(_pBuff) + 1];
strcpy_s(m_pBuff, strlen(_pBuff) + 1, _pBuff);
}
~CObj()
{
if (m_pBuff)
{
delete[] m_pBuff;
m_pBuff = nullptr;
}
cout << "소멸자 호출" << endl;
}
얕은 복사 방식의 구현
CObj(CObj& _obj)
{
// 얕은 복사
m_pBuff = _obj.m_pBuff;
}
private:
char* m_pBuff;
};
void main()
{
CObj obj1("Hello");
CObj obj2(obj1);
}
-> main 함수를 시작하면서 obj1 객체를 만든다.
-> 이후, obj2 객체를 만드는데 디폴트 복사 생성자를 호출하여 만든다.
-> 함수가 종료되면서 stack 영역을 정리할 때 문제가 발생한다.
-> obj2 객체가 소멸하면서 obj2.m_pBuff가 가지고 있는 Heap 영역의 주소를 해제하려 했더니
-> obj2가 해제하면서 이미 할당되지 않은 공간이 되었다.
-> obj2는 obj1의 주소를 참조하고 있는 상태에서 해당 주소를 할당 해제 하였기 때문이다.
이러한 문제를 해결하는 방법은 '깊은 복사'를 사용하는 것이다.
깊은 복사
-> 얕은 복사와는 달리 객체가 가진 모든 멤버(값과 참조형식 모두)를 복사한다..
객체가 참조 타입의 멤버를 포함할 경우 참조값의 복사가 아닌 참조된 객체 자체가 복사되는 것을 깊은 복사라 한다.
-> 복사 받을 때 객체 또한 동적할당을 진행한다.
-> 각 객체들이 서로 다른 공간을 참조하게 만든다.
class CObj
{
public:
CObj() : m_pBuff(nullptr) {}
CObj(char* _pBuff)
{
m_pBuff = new char[strlen(_pBuff) + 1];
strcpy_s(m_pBuff, strlen(_pBuff) + 1, _pBuff);
}
~CObj()
{
if(m_pBuff)
{
delete[] m_pBuff;
m_pBuff = nullptr;
cout << "소멸자 호출" << endl;
}
}
CObj(CObj& _obj)
{
// 깊은 복사 방식의 구현
this->m_pBuff = new char[strlen(_obj.m_pBuff) + 1];
strcpy_s(m_pBuff, strlen(_obj.m_pBuff) + 1, _obj.m_pBuff);
}
private:
char* m_pBuff;
};
void main()
{
CObj obj1("Hello");
CObj obj2(obj1);
}
-> main 함수를 시작하면서 obj1 객체를 만든다.
-> 이후, obj2 객체를 만드는데 디폴트 복사 생성자를 호출하여 만든다.
-> 함수가 종료되면서 stack 영역을 정리한다.
-> obj2 객체가 소멸하면서 obj2.m_pBuff가 가지고 있는 Heap 영역의 주소를 해제한다.
-> 복사 생성자에서 m_pBuff를 함수의 인자(객체)를 전달하여 새로 할당한다.
'Programming > C++ Basic' 카테고리의 다른 글
C++ friend (0) | 2020.09.17 |
---|---|
C++ this 포인터, extern 키워드 (0) | 2020.09.16 |
C++ static과 클래스 , 멤버 함수 (0) | 2020.09.15 |
C++ const 와 클래스, 멤버 함수 (0) | 2020.09.15 |
C++ 파일 입출력(fopen_s, fread(), fwrite()) (0) | 2020.09.13 |