티스토리 뷰

C++

[C++] 안전한 포인터 설계

nodeal 2019.01.26 18:09

C에서 포인터의 난관이라면 '포인터의 개념' 자체에서 비롯된 문제였다면

C++에서 포인터로 인한 문제는 많은 경우 할당과 해제로 인한 것이라고 보인다.


다음의 예시를 보자.

void foo() {
    int* ptr = new int;
    bar(ptr); // throws exception
    delete ptr;
}

bar함수는 int 포인터를 인자값으로 받아 무언가를 처리하는 함수이다. 이때 잘못된 값이 들어가 bar가 예외를 throw했다면 delete이 작동할까?


우린 C에서 malloc과 free, C++에서 new와 delete이 한 쌍이 되도록 배웠다. 하지만 분명 쌍을 맞췄지만 의도찮게 delete까지 도달되지 않는 경우, 어떻게 해야할까?


이때 활용되는 것이 RAII기법을 활용한 포인터의 관리이다.


다음의 예시를 보자.

class safe_ptr {
    private:
    int* ptr;

    public:
    safe_ptr() : ptr(new int) {}

    ~safe_ptr() {
        delete ptr;
    }
};

void foo() {
    safe_ptr ptr;
    bar(ptr); // throws exception
}

기능은 같지만 bar에서 예외가 생기더라도 foo의 scope가 끝나는 지점에서 safe_ptr의 소멸자가 호출되어 raw pointer는 안전하게 해제된다.


" ???: Exception을 처리하면 되잖아? "


예외를 적절히 처리하는 것 또한 중요하다. 하지만 bar의 정의가 다음과 같이 정의되어있다면, 예외를 throw한 것 만으로 bar는 충분한 일을 한 걸 것이다.

void bar(int* ptr) {
    /* use ptr properly ... */
    int* never_created = new int[99999999999...999];
}

이럴 때 예외를 처리하는게 맞을까 프로그램이 자연히 종료되도록 두는게 맞을까는 개발자가 필요에 따라 할 일이다. 저렇게 큰 공간이 하드코딩되지 않고 ptr에 의존하는 값이였다면 정말 bad_alloc을 예상하고 bar를 try-catch에 감쌀까? 분명 그런 방법이 더 어려울 것이다.



다만, 다음의 경우는 위험할 수 있다.

void foo() {
    safe_ptr original;
    auto copied = original;
    // once 'original' delete, copied.ptr also deleted.
}

safe_ptr은 별도의 복사 생성자와 복사 대입 연산자가 구현되어있지 않아 각 멤버 변수를 그대로 복사하는 기본 정의만 있다. 따라서 scope의 끝에 다다라 original의 소멸자가 호출되어 original.ptr가 해제되면 주소만 복사된 copied.ptr 또한 해제되어 copied의 소멸자를 호출하는 과정에 'double free or corruption' 오류가 발생한다.


이 문제를 해결하는 방법이 이전에 소개한 Rule of three 또는 Rule of five이다. 자세한건 해당 포스트에서 확인하도록 하자.



다시 돌아와서, 이 문제는 raw pointer에만 해당하지 않는다. 만일 open되면 close되어야 하는 기능들에 대해서는 어떡해야할까? 마찬가지이다. Scope의 끝에서 알아서 close되게 하던가, 그게 class 정의에 맞지 않는다면 다른 wrapper class를 사용하면 된다.


많은 경우에서 이런 class를 직접 설계할 필요는 없다. C++11부터는 shared_ptr와 unique_ptr를 제공해주기 때문. 따라서 이 둘을 활용하여 값의 반환 대신 pointer만 반환하고자 할 때, 명시적인 해제 시점을 잡기 어려울 때도 해결할 수 있을 것이다.




'C++' 카테고리의 다른 글

[C++] 안전한 포인터 설계  (0) 2019.01.26
[C++] Rule of three/five/zero  (0) 2019.01.05
댓글
댓글쓰기 폼