복사 생성자

-> 생성자 : 객체 생성 시 객체를 받아온다.

-> 인자로 넘어오는 객체의 멤버 값을 복사 받는 것.

 

디폴트 복사 생성자

-> 사용자가 복사 생성자를 정의하지 않으면 컴파일러가 복사 생성자를 생성하여 호출한다.

-> 사용자가 복사 생성자를 정의하면 컴파일러는 복사 생성자를 생성하지 않는다.

 

 

 

복사 생성자의 호출 시점 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

+ Recent posts