본문 바로가기

Cpp

C++에서의 메모리 관리 방식

 

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