본문 바로가기

Information Technology/C++

[C++] 깊은 복사, 얕은 복사, 그리고 대입 연산자

개인 학습을 목적으로 정리한 글입니다. 이점 참고하고 읽어주세요 ;)


MyString(const MyString& source)
	{ // 메모리를 다시 할당받고 값을 다시 복사하는 걸 deep copy
		cout << "Copy constructor " << endl;

		m_length = source.m_length;

		if (source.m_data != nullptr)
		{
			m_data = new char[m_length];

			for (int i = 0; i < m_length; ++i)
				m_data[i] = source.m_data[i];
		}
		else
			m_data = nullptr;
	}

이 코드의 경우에는 단순히 복사생성자에서 포인터의 주소만 가져오는 것이 아니라,

new char을 통해 메모리를 할당받고

새로운 m_data에 복사하는 객체의 배열 요소들을 for문을 통해 가져옵니다.

이러한 복사 방식을 deep copy라고 합니다.

 

	MyString& operator = (const MyString& source) // 대입 연산자
	{
		//shallow copy
		/*this->m_data = source.m_data;
		this->m_length = source.m_length;*/

		cout << "Assignment operator " << endl;

		if (this == &source) // self-assignment를 막아서
			return *this;	 // 자신을 return하고 끝냄

		delete[] m_data; // 다른 instance를 대입하기 전에 이미 주소에 값이 있을 수 있음
		
		m_length = source.m_length;
		if (source.m_data != nullptr) // 복사하려는 것이 nullptr이 아닐 떄
		{
			m_data = new char[m_length]; // 길이를 입력받음

			for (int i = 0; i < m_length; i++)
				m_data[i] = source.m_data[i];
		}
		else
			m_data = nullptr;

		return *this;
	}

위에 있는 코드는 특정 클래스에 다른 멤버를 대입할 때 사용하는 대입 연산자입니다.

클래스를 설계할 때 대입 연산자를 만들지 않으면 shallow copy로 멤버를 복사하도록 visual studio는 기본 설계가 되어있는 것 같아요.

 

코드를 한 줄씩 설명 드리자면 첫 번째 if문은 자기 자신을 복사하는 것을 방지하는 것입니다.

사람이 볼 때는 자신에게 왜 자기를 또 복사하지..? 하고 넘어가면 그만인데,

프로그램이나 컴파일러의 입장에서는 꽤 복잡한 문제로 번지나봐요.

그래서 복사하려는 instance의 주소가 자신과 같으면 그냥 자기 자신을 return하고 생성자를 끝내버립니다.

 

그 아랫줄에 delete[]같은 경우는, 일단 대입을 하려면 초기화가 된 instance이고,

그렇다면 해당 주소에 이전에 저장된 값이 있을 가능성이 있겠죠?

때문에 이를 막기 위해 해당 메모리를 delete[]를 통해 완전히 씻어냅니다.

그 다음부터는 대입하려는 instance의 길이를 입력받고,

m_data를 순서대로 복사하는 과정으로 대입 연산자가 마무리됩니다.

 

int main()
{
	MyString hello("Hello");
	MyString str1 = hello;
	MyString str2;
	str2 = hello;

	return 0;
}

위 코드블록에서 보시면,

hello라는 이름의 멤버에 "Hello"라는 문자열을 입력하여 초기화시켜주고,

그 다음에는 str1이라는 멤버를 초기화하면서 hello를 대입해줍니다.

우리는 위에서 분명 대입 연산자를 설정했지만, str1의 경우는 값을 정하는 초기화 단계이기 때문에 대입 연산자가 아니라 copy constructor가 호출됩니다. 이점 주의하세요!

반면 그 아랫줄에 있는 str2는 초기화가 된 이후에, 그 아랫줄에서 hello를 대입하니까 대입 연산자가 호출됩니다.

 

MyString(const MyString& source) = delete; // shallow copy가 되는걸 막는 차선책?

작업 시간이 부족하거나 하면 모든 클래스에 일일이 copy constructor를 만들 여유가 없을 수도 있을텐데,

그럼에도 shallo copy가 발생하는 걸 막는 차선책이 위에 있는 코드블록처럼 아예 copy constructor 자체를 막는 방법입니다.

하지만 가장 좋은 건 copy constructor를 설정하는 것이겠죠?

'Information Technology > C++' 카테고리의 다른 글

[C++] 입출력 연산자 오버로딩  (0) 2019.11.15
[C++] 산술 연산자 오버로딩  (0) 2019.11.15
[C++] std::move(2)  (0) 2019.11.15
[C++] std::move(1)  (0) 2019.11.15
[C++] 이동 생성자와 이동 대입  (0) 2019.11.15