티스토리 뷰

복사생성자

복사생성자는 객체의 복사본을 생성할 때 호출되는 생성자이다.
클래스를 작성할 때 복사생성자를 생략하면 디폴트 생성자처럼 컴파일러가 알아서 만들기 때문에 필요하지 않다면 정의하지 않아도 된다.
그렇다면, 복사생성자를 정의해야 하는 경우는?
클래스 내부에서 메모리를 동적 할당 및 해제하고 이를 멤버 포인터 변수로 관리하고 있는 경우이다.

우선 복사생성자의 문법은 아래와 같다.

클래스이름(const 클래스이름 &rhs);


복사생성자를 사용한 예제를 보면서 복사생성자를 사용해야하는 경우를 더 자세히 살펴보겠다.


예제 코드

class CMyData
{
public:
	CMyData(int nParam)
	{
		m_pnData = new int;
		*m_pnData = nParam;
	}
	
	// 객체가 소멸하면 동적 할당한 메모리를 해제한다.
	~CMyData()
	{
		delete m_pnData;
	}
	
	int GetData()
	{
		if(m_pnData != NULL)
		{
			return *m_pnData;
		}
	}
	
private:
	int *m_pnData = nullptr;
};

int _tmain(int argc, _THCAR* argv[])
{
	CMyData a(10);
	CMyData b(a);
	cout << a.GetData() << endl;
	cout << b.GetData() << endl;
	
	return 0;
}


위의 코드를 실행하면 에러가 날것이다.

소멸자에서 해제된 메모리를 다시 한 번 더 해제하기 때문이다.


원래는 위와 같이 각각 생성되기를 바랬지만 실행하고 나면


메모리구조가 위처럼 바뀌게 된다. 그렇기 때문에 소멸자가 호출 되었을 때,

이미 해제된 메모리를 다시 한 번 더 해제하려고 하므로 에러가 난다.

이를 얕은 복사(shallow copy)라고 한다.


이러한 문제를 해결하기 위해 깊은 복사(deep copy)를 해야한다!

깊은 복사를 하면 포인터가 가리키고 있는 메모리의 내용이 복사된다.

그러면 각각의 포인터가 각각 다른 대상을 가리키게 되므로 메모리를 해제하는 과정에서 오류가 발생하지 않는다.


깊은 복사를 위한 코드는 아래와 같이 복사생성자를 추가하면 된다.

CMyData(const CMyData &rhs)
{
	// 메모리 할당
	m_pnData = new int;
	
	// 포인터가 가리키는 위치에 값을 복사
	*m_pnData = *rhs.m_pnData;
}

복사생성자를 추가함으로써 새로 메모리를 할당하고 

단순히 포인터 변수의 주소를 같은 값으로 만드는 것이 아닌!


포인터가 가리키는 대상 메모리에 저장된 값을 가져와

다시 포인터가 가리키는 대상 메모리로 복사했다!


위의 예제와 같이 멤버 포인터 변수를 사용하는 경우 복사생성자를 만들어야

메모리를 해제할 때 발생하는 문제를 해결할 수 있다.


참고 : 이것이 c++이다

공지사항
최근에 올라온 글
최근에 달린 댓글
Total
Today
Yesterday
링크
«   2024/10   »
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 28 29 30 31
글 보관함