C++ 에서는 사용자가 직접 메모리 관리가 가능하다. 기본적으로 new와 delete 키워드를 이용한다.
new 를 통해 힙 메모리 영역에 데이터를 할당한다.
int* ptr;
ptr = new int;
* 은 포인터로 주소를 담는 변수이다. 이 주소를 담는 변수는 스택에 저장된다. 그리고 변수의 값으로는 힙 메모리 영역의 주소가 담긴다.
사용이 끝난 포인터는 delete 를 사용하여 해제한다.
int* ptr = new int;
delete ptr;
메모리 누수
할당했던 메모리를 제대로 해제하지 않으면 발생한다. 시간이 지날수록 점차 메모리 사용량이 많아지며 문제 발견 및 해결이 쉽지 않다.
프로파일러나 메모리 디버깅을 통해 메모리 누수를 확인 가능하다.
참조 카운팅 기법을 통해 예방할 수 있다.
스마트 포인터를 사용하여 예방할 수도 있다.
댕글링 포인터
이미 할당 해제된 포인터를 의미하며 이 포인터에 접근하면 문제가 생긴다.
delete 로 해제는 했지만 포인터를 사용하는 것을 막을 수 없다는 것이다. 만약 해제된 메모리 주소에 다른 데이터가 저장되면 댕글링 포인터 접근 시 큰 문제를 야기한다.
int* ptr = new int;
delete ptr;
ptr = nullptr;
포인터에 nullptr 을 가리키게 하여 예방한다.
연속 해제
int* ptr = new int;
delete ptr;
delete ptr;
할당된 메모리를 delete 로 해제하고 나서 다시 해당 포인터로 접근하여 해제할 때 문제가 생길 수 있다.
1. ptr 포인터로 메모리에 접근하여 delete 로 해제
2. 다른 포인터나 프로세스가 해제된 메모리를 사용
3. ptr 포인터로 해당 메모리에 다시 접근하여 또 delete 하면 문제 생김
스마트 포인터
unique_ptr : 하나의 포인터만 해당 객체를 소유하게 한다. unique_ptr 파괴 리셋 되면 리소스는 자동 해제된다.
#include <iostream>
#include <memory>
int main() {
std::unique_ptr<int> ptr(new int(42)); // int 형 동적으로 할당된 포인터
// 포인터가 가리키는 값을 출력
std::cout << "Value: " << *ptr << std::endl;
// unique_ptr은 소유권을 가지고 있으므로 복사 또는 이동할 수 없음
// std::unique_ptr<int> ptr2 = ptr; // Error: 'std::unique_ptr<int>::unique_ptr(const std::unique_ptr<int>&)' is private
return 0;
}
shared_ptr : 여러 포인터가 하나의 리소스를 참조할 때 사용하는 스마트 포인터이다. 참조 카운팅 형식을 사용하며 더 이상 해당 리소스를 참조하는 포인터가 없으면 리소스를 해제한다.
#include <iostream>
#include <memory>
int main() {
std::shared_ptr<int> ptr1(new int(42)); // int 형 동적으로 할당된 포인터
// 포인터가 가리키는 값을 출력
std::cout << "Value: " << *ptr1 << std::endl;
// shared_ptr을 복사할 수 있음
std::shared_ptr<int> ptr2 = ptr1;
// 포인터가 가리키는 값을 수정
*ptr1 = 21;
// 두 개의 shared_ptr이 같은 메모리를 가리키고 있으므로 둘 다 값이 변경됨
std::cout << "Value after modification: " << *ptr1 << ", " << *ptr2 << std::endl;
return 0;
} // ptr1, ptr2가 범위를 벗어나면 참조 카운터가 0이 되어 메모리가 해제됨
weak_ptr : shared_ptr 이 가리키는 리소스를 가리킨다. shared_ptr 의 참조 카운팅에 영향을 주지 않는다. 순환참조 문제를 예방하기 위한 포인터로 사용된다. 리소스를 직접 해제하지 않는다.
weak_ptr은 직접적으로 자원을 할당 받을 수 없다.
weak_ptr이 자원에 대한 참조를 받으려면 shared_ptr이나 다른 weak_ptr의 자원을 복사 생성자나 대입 연산자를 통해서
할당 받을 수 있다.
lock 함수를 통해서 shared_ptr 를 반환할 수 있다. 이때 shared_ptr 의 참조 카운팅이 0 이 되어 객체가 해제되면 weak_ptr을 사용 불가하다.
shared_ptr 의 참조 카운팅이 0 이면 weak_ptr가 lock 함수를 호출해도 nullptr 을 가리키는 shared_ptr 를 반환한다.
weak_ptr 이 참조 카운팅에 영향을 안끼친다고 했으나 내부적으로 _Weaks 라는 변수가 있고(기본적으로 1이다.) 여기서 weak_ptr 카운팅을 한다.
'Cpp' 카테고리의 다른 글
객체지향의 4대 특성 (0) | 2024.05.27 |
---|---|
가상 함수와 순수 가상 함수의 차이 (0) | 2024.05.16 |
주소 바인딩 (0) | 2024.04.29 |
재귀 함수 (0) | 2024.04.22 |
래퍼 함수 (0) | 2024.04.22 |