오늘은 참 신기한게 , 얕은 복사 깊은 복사 개념을 배우고 난 후 바로 해당 문제가 원인이 되어, 프로그램이 정상작동하지 않았습니다.
먼저 얕은 복사,깊은 복사 개념부터 알고 가겠습니다.
얕은 복사(Shallow Copy)란, 대입 연산자를 활용해서 두 개의 포인터가 같은 위치를 공유하는 것을 말합니다. 아래 그림과 같이 얕은 복사를 하면 dangling pointer가 발생할 수 있습니다.

따라서 B의 입장에서는 어디를 가리켜야 할지 모르게됩니다. 해당 문제가 Dangling Pointer 문제입니다.
깊은 복사(Depp Copy)란 독립된 메모리 영역을 할당해서 위치가 아닌 가리키는 내용이 복사되는 것을 말 합니다. 독립된 영역이므로 dangling pointer가 발생하지 않습니다.

이제 제가 문제를 겪은 코드를 보겠습니다 .
void resize(int newCapacity) {
// 기존의 simpleVector에게서 data를 복사하고
// 새로운 newVector1로 소유권을 넘겨주는 과정입니다.
SimpleVector newVector(*this, newCapacity); // 복사 생성자 호출
delete[] data; //기존의 데이터 삭제
data = newVector.data; // 데이터를 복사한 newVector
currentCapacity = newVector.currentCapacity; // class 멤버변수 update
newVector.data = nullptr; //문제의 과정
}
해당 코드에서 newVector.data 를 nullptr로 설정을 안하게 되면 다음과 같은 문제가 발생합니다.

매개변수로 simpleVector를 받아 simpleVector를 생성하는 생성자 내부에서
newVector는 지역변수 입니다.
우리가 원하는 것은 simpleVector의 크기를 늘려주기 위해
newVector라는 임시 벡터를 생성시켜 멤버변수를 저장하고, 다시 simpleVector로 돌아오는 과정을 원하는데
newVector는 지역변수이기 때문에 해당 생성자가 끝나면 사라지게 됩니다.
위의 그림처럼 연결되어 있는 상황에서 newVector가 사라진다면

그림과 같이 함수가 끝나면 Local변수는 사라지게되고, 기존의 Data는 사라지게됩니다.
data에 simpleVector입장에서는 data가 해지된지 모르기 때문에 결과적으로는 simpleVector는 data를 잃어버리고 되고,
무엇을 가리켜야할지 몰라 컴파일에러가 발생합니다.
C++은 이런 메모리의 위험이 있기에 Dagling Pointer가 발생하지 않게 알아서 관리해주는 스마트 포인터를 제공 합니다.
스마트 원리의 핵심 원리는 레퍼런스 카운터 입니다.
delete를 직접 하는 대신,
자신을 참조하고 있는 포인터의 개수가 0이 되면 자동으로 해지 하는 방식 입니다.
가장 먼저 unique_ptr에 대해 알아보겠습니다 .
unique_ptr은 레퍼런스 카운터가 최대 1인 스마트 포인터 입니다.
따라서 소유권을 다른 사람에게 주는 건 가능하나, 동시에 두개 이상 소유할 수 없습니다.
최대 레퍼런스 카운터가 1이기 때문에 복사 혹은 대입이 되지 않습니다. 이걸 시도하는 순간 컴파일 에러가 발생 합니다.
/**unique_ptr 사용 방법*/
#include <iostream>
#include <memory> // unique_ptr 사용
using namespace std;
int main() {
unique_ptr<int> ptr1 = make_unique<int>(10);
cout << "ptr1의 값: " << *ptr1 << endl;
// unique_ptr은 복사가 불가능
// unique_ptr<int> ptr2 = ptr1; // 컴파일 에러
// 범위를 벗어나면 메모리 자동 해제
return 0;
}
unique_ptr은 메모리의 소유권이 하나의 포인터에게만 주어집니다.
하지만 우리가 하나의 포인터가 하나만을 관리하는 경우는 흔하지 않습니다.
인수인계를 해야죠 !
move를 사용해서 소유권 이전 할 수 있습니다.
해당 메모리의 소유권을 옮기는 move를 사용합니다.
#include <iostream>
#include <memory>
using namespace std;
int main() {
unique_ptr<int> ptr1 = make_unique<int>(20);
// 메모리 소유권 이동
unique_ptr<int> ptr2 = move(ptr1);
if (!ptr1) { //넘겨준 포인터가 비어있다면 =>사실 검증
cout << "ptr1은 이제 비어 있습니다." << endl;
}
cout << "ptr2의 값: " << *ptr2 << endl; // 값을 제대로 가리키는지 확인
return 0;
}
만일 클래스에서 사용하려면? 다음과 같이 선언해서 사용하면됩니다 !!
int main() {
unique_ptr<My> myObj = make_unique<My>(42);
//클래스가 있다고 가정
myObject->display();
// 메모리 소유권 이동
unique_ptr<My> newOwner = move(myObj);
if (!myObj) {
cout << "myObj은 이제 비어 있습니다." << endl;
}
newOwner->display(); // 제대로 전달되었나 확인
// 범위를 벗어나면 newOwner가 관리하는 메모리 자동 해제
return 0;
}
다음은 shared_ptr과 객체지향형 설계에 대해 공부할 예정입니다.
'TIL' 카테고리의 다른 글
2025 / 01 / 09 TIL : 데코레이터,싱글톤 패턴 (0) | 2025.01.09 |
---|---|
2025 / 01 / 07 TIL : CONST (0) | 2025.01.07 |
2025/01/02 TIL : Sort함수와 람다 (0) | 2025.01.03 |
2024/12/31 TIL : 중첩 타입과 중첩클래스 (2) | 2024.12.31 |
TIL : 2024 /12/30 : new[]와 new 의 차이 (1) | 2024.12.30 |