승쨩개발공부
[C++] 복사 생성자(얕은복사, 깊은복사) 본문
복사 생성자
생성자 : 객체 생성 시 객체를 받아온다
-> 인자로 넘어오는 객체의 맴버 값을 복사 받는 것.
디폴트 복사 생성자
사용자가 복사 생성자를 정의하지 않으면 컴파일러가 복사 생성자를 생성하여 호출한다.
사용자가 복사 생성자를 정의하면 컴파일러는 복사 생성자를 생성하지 않는다.
복사 생성자의 호출 시점 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를 함수의 인자(객체)를 전달하여 새로 할당한다.
'C++' 카테고리의 다른 글
[C++] 상속 (0) | 2021.12.03 |
---|---|
[C++] this포인터, extern, friend (0) | 2021.12.03 |
[C++] static과 클래스, 멤버함수 (0) | 2021.12.02 |
[C++] const와 클래스, 멤버함수, 이니셜라이져 (0) | 2021.12.02 |
[C++] 전방 선언 (0) | 2021.12.02 |