깊은 복사와 얕은 복사(Deep Copy, Shallow Copy)
깊은 복사 / 얕은 복사란
얕은 복사 : 복사된 객체가 원본 객체의 요소들을 참조
- 예시
int *a = new int(5); int *b = new int(3); a = b; delete a; delete b;
3이 저장된 공간을 두 번 지움
5가 저장된 공간이 안지워짐
위와 같은 복사를 얕은 복사라 한다.(참조만 복사)
- 객체 내부에 참조 타입이 있는 경우에는 얕은 복사로 인해 문제가 발생할 수 있음
깊은 복사 : 원본 객체의 완전한 복사본을 생성, 복사된 객체는 완전히 독립적인 새로운 객체를 가지게 됨
- 내부에 있는 모든 객체들까지 재귀적으로 복사함
예시
int *a = new int(5); int *b = new int(3); *a = *b; // 깊은 복사 delete a; delete b;
b의 값이 복사되어 a의 값으로 저장됨
위와 같은 복사를 깊은 복사라고 한다
- 내부 객체의 변경이 복사본에 영향을 주지 않음
얕은 복사의 대표적 예시
class Copy {
private:
int *a;
};
Copy A, B;
// 여기에서
B = A; // 를 하게 되면...
// B.a = A.a 가 되어 얕은 복사가 일어나게 된다.
얕은 복사를 유의하며 String 클래스 구현하기
// string class를 구현하는 코드
#include <iostream>
using namespace std;
class String {
public:
String() {
cout << "String() 생성자 호출" << '\n';
strData = NULL;
len = 0;
}
String(const char *str) {
cout << "String(const char *str) 생성자 호출" << '\n';
// strData = str; --> 얕은 복사
len = strlen(str);
strData = new char[len + 1]; // 널문자가 있기 때문에 len + 1
cout << "strData 할당: " << (void *)strData << endl;
// strData는 char 포인터인지(주소출력), 문자열을 가리키는건지(문자열출력) cout을 보고 컴파일러가 문자열로 인식
// 따라서 (void*)strData 사용
/*
void *ptr = strData;
*ptr = 'A'; 는 불가능. void*는 주솟값만 가지고 있기때문에, char형인지 int형인지 알 수 없기 때문.
*/
strcpy(strData, str); // 깊은 복사
}
~String() {
cout << "~String() 소멸자 호출" << endl;
delete[] strData;
cout << "strData 해제됨: " << (void *)strData << endl;
strData = NULL; // delete하고 해제된 메모리에 대한 접근을 막기위한 관습적 방법.
}
char *GetStrData() const {
return strData;
}
int GetLen() const {
return len;
}
private:
char *strData;
int len;
};
int main(void) {
String s1("안녕");
String s2(s1);
// 기본적으로 소멸은 선언의 역순
cout << s1.GetStrData() << '\n';
cout << s2.GetStrData() << '\n';
}
생성자의 매개변수로 받은 문자열에서 strcpy 함수를 이용해 문자열의 길이를 저장
문자열의 길이 + 1 만큼의 크기로 동적할당 후(널문자 때문에 + 1) strcpy 함수를 통해 깊은 복사
위 코드를 실행하면 에러가 발생함
일단, 생성자가 s1을 생성할 때 한번만 호출됨
- String s2(s1)에서 얕은 복사가 일어남을 알 수 있다.
첫 번째 소멸자 호출까지는 정상적으로 메모리가 해제되다가 두 번째 소멸자 호출과정에서 에러가 발생
이는 얕은 복사가 일어났기 때문에 같은 공간에 대해 메모리를 두 번 해제했기 때문이다.
이를 막기 위해 복사 생성자 오버로딩을 이용함 (다음 챕터에)
참조) 두들낙서 C/CPP 강의
'Programming Language > C++' 카테고리의 다른 글
[C++] 대입 연산자 오버로딩 (0) | 2024.03.12 |
---|---|
[C++] 복사 생성자 오버로딩 (0) | 2024.03.12 |
[C++] 동적할당 (0) | 2024.02.04 |
[C++] 연산자 오버로딩 (0) | 2024.02.04 |
[C++] 멤버 메서드의 활용 (0) | 2024.02.04 |