Post List

2015년 1월 4일 일요일

C++ 스마트 포인터 (Smart Pointer) 란 ?

[스마트 포인터의 사용이유]

뭘까요? 정답은 프로그래머가 실수하지 않기 위해서 사용합니다. 뭔지 설명하지도 않앗으면서 결론만 얘기하니 뭔소린지 모르겟죠? 한 예제를 봅시다.

 class My
 {
 private:
        int m_A;
        int m_B;

 public:
        My(int a=0, int b=0)
        {
                m_A = a;
                m_B = b;
        }
        ~My()
        {
        }

        void Print() const
        {
                cout << "a: " << m_A << "  b: " << m_B << endl;
        }
 };
/////////////////////////////////////////////////////////////

 int main()
 {
        My* my = new My(10, 20);

        my->Print();

        delete my;

        return 0;
 }

보통 객체를 동적으로 만들때 사용하는 new와, 그 메모리를 해제하는 delete가 있지요. 스택에 할당되는 지역변수는 함수가 끝날경우 메모리에서 자동 해제를 하죠. 하지만 new로 생성한 변수는 Heap영역이라는 곳에 공간이 할당됩니다. 이 공간에 변수를 생성할경우, 자동으로 지워지지 않기 때문에 프로그래머가 delete로 직접 해제해줘야 합니다. C++에서 한단계 발달(그렇다고 해서 C+보다 우월한건 아닙니다. C++나름의 장점이 있습니다. 그리고 지금도 사용하고 있고요)한 Java같은 경우 가비지 컬렉션(Garbage collection)이 있기 때문에 이런 부분에 대해선 전혀 신경쓸 필요는 없습니다.

C++은 이런 가비지 컬렉션이 없기 때문에 delete로 소멸 시켜줘야 합니다. 동적할당 제대로 하고 제대로 소멸 시킬수 있다면 좋겟지만 프로그래밍이란 언제나 예외가 존재하고, 생각지도 못한 논리적 오류를 자주 발생시키기 때문에 동적할당을 햇음에도 제대로 소멸자를 호출하지 못하는경우가 얼마든지 생길수 있습니다.

그래서 프로그래머들은 동적할당후 소멸자를 제대로 호출하지 못하는 경우가 생기는 문제를 해결하기 위해 한가지 해결방안이 등장합니다. 바로 스마트 포인터(Smart Pointer)죠.

  int Function(int a)
 {
        My* my = new My(10, 20);

        my->Print();

        if(3 != a)
      {

              return -1;
      }

      delete my;

        return 0;
 }

자 문제가 뭘까요? 바로 if문에 있습니다. if문에서는 a가 3이 아닐경우엔 return하게 됩니다. 그럼 delete는? 호출이 되지 않아 new로 할당된 객체 my의 공간이 해제되지 않죠. 저런 코드 안짜면 된다고요? 하나더 말해보죠. 만약 my->Print()함수에서 오류가 나버렷다면? 예상치 못한부분에서 오류가 나서 함수가 종료되는경우가 있습니다. 그럴경우에도 delete가 호출이 되지 않죠.

아무튼 이런 여러가지 이유로 인해 동적으로 할당된 객체(또는 변수)가 소멸자를 호출하지 못하는경우가 생길수 있습니다. 스마트 포인터는 위와 같은 소멸자 미호출상태를 해결해줄수 있는 하나의 방법입니다.





[스마트 포인터 사용하기]

 class My
 {
 private:
        int m_A;
        int m_B;

 public:
        My(int a=0, int b=0)
        {
                m_A = a;
                m_B = b;
        }
        ~My()
        {
        }

        void Print() const
        {
                cout << "a: " << m_A << "  b: " << m_B << endl;
        }
 };

//////////////////////////////////////

 class MyPtr
 {
 private:
        My* m_My;

 public:
        MyPtr(My* my)
        {
                m_My = my;
        }
        ~MyPtr()
        {
                delete m_My;
        }

        My* operator->() const
        {
                return m_My;
        }
 };

/////////////////////////////////////////

 int main()
 {
        MyPtr my = new My(10, 20);

        my->Print();

        return 0;
 }

MyPtr클래스를 만들고 멤버변수로 My* m_My를 만듭니다. 그리고 MyPtr객체를 만들때 생성자로 My를 넘겨주도록 합니다. 소멸자에서는 생성자에서 받은 My를 delete로 소멸시켜주고요.

이럼 괜히 복잡하기만 하지 왜이렇게 할까요? 바로 MyPtr은 '지역변수'이기 때문이죠. 무슨말이냐고요? 지역변수는 함수가 끝날떄 자동으로 소멸되죠. 즉 MyPtr을 지역변수로 만들때, 함수가 끝날경우 자동으로 소멸자가 호출된다는거죠. 따로 delete를 사용하지 않아도 말입니다. 연산자 오버로딩은 뭐... 설명안해도 알겟죠?

원문 : http://lwove.pe.kr/30147311708

댓글 없음:

댓글 쓰기