ABOUT ME

-

Today
-
Yesterday
-
Total
-
  • [C/C++]자주 하는 실수
    개발/C/C++ 2008.10.22 21:27

     

    프로그램을 하다 스스로 자주 실수하게 되는 코딩 스타일이 있습니다. 

    얼마전에 제가 자주 겪는 함정에 빠졌는데요. 관련 코드를 축약하면 아래와 같습니다.

     

    CMyClass* pMy = NULL;

     

    // 클래스 생성을 함수를 통해 대행

    CreateMyClass(pMy);

     

    // 아래 함수 호출하면 Crash!!

    pMy->DoSomeghing();                         

     

    // 잘못 만들어 진 함수

    void CreateMyClass(CMyClass* pMy);)

    {

               pMy = new CMyClass;

               ASSERT(pMy);

    }


     

     

    CreateMyClass() 라는 함수를 통해 CMyClass 의 생성을 대행하는 것이 목적이었는데, 주석으로 보이는 것과 같이 프로그램이 쉽게 죽어버렸습니다.

    이상하게, 포인터를 가지고 작업하다 보면 이와 같은 실수를 자주 겪곤 합니다. (블로그에 실수를 다시 기록하는 것도 같은 실수를 반복하지 않기 위함이기도 하지요)

     

    Call By Value Call By Reference 를 떠나, 어찌 보면 명백한 오류인데, 포인터만 들어가면 요런 실수가 자주 일어나는건, 학부때 포인터 공부를 게을리했거나, 포인터를 최근에 거의 사용하지 않아서 그런가 봅니다.  

    포인터에 새로운 값을 할당하는 함수는 늘 그렇듯, 이중 포인터로 넘겨야 된다는 간단한 습관을 다시금 깨달았습니다.

     

    CMyClass* pMy = NULL;

     

    // 클래스 생성을 함수를 통해대행

    CreateMyClass(&pMy);

     

     

    // 제대로 수정한코드

    void CreateMyClass(CMyClass** ppMy);)

    {

               (*ppMy) = new CMyClass;

               ASSERT(*ppMy);

    }


     

    블로그에 부끄러운 실수를 올렸으니 담부터는 이러지 말기를 바랍니다 ㅎㅎ


    '개발 > C/C++' 카테고리의 다른 글

    [C/C++]자주 하는 실수  (15) 2008.10.22
    [C++]소멸자에서 가상함수 호출하기  (8) 2008.10.08
    [C++]포인터 Wrapping 클래스 만들기  (4) 2008.09.09
    [C/C++] enum, 보다 나은 enum  (19) 2008.07.21
    싱글톤 클래스  (5) 2008.07.01
    [C++]STL Container 조합하기  (5) 2007.11.12
    [C/C++]유용한 #pragma directive  (9) 2007.09.05

    댓글 15

    • 프로필사진

      ㅋㅋ 그렇죠? 저도 예전에 이런 일을 종종해서 요즘은 포인터 레퍼런스를 넘깁니다. :)

      2008.10.23 09:57 신고
    • 프로필사진

      C에서는 무조건 call by value입니다만 C++에서 call by reference가 생긴 이상 이중포인터 같은 이상한 놈은 쓰지 않는 것이 좋겠죠.
      또한 위의 케이스라면 인자로 받는 것이 아니라 리턴값으로 받는 것이 자연스러워 보입니다. 생성을 특별한 함수를 통해 한다면 해당 클래스의 static method를 호출하여 리턴값으로 객체를 받는 것은 매우 흔한 사용이니까요.

      2008.10.23 09:59
      • 프로필사진

        좋은 지적감사합니다.
        리턴으로도 많이 사용하는데
        COM 의 IUnknown 인터페이스처럼
        HRESULT QueryInterface(void** ppRet);

        리턴값으로는 오류값을 리턴해야 하는경우들이 있어서요.

        저 역시 포인터 2개는 머리가 아파 다른 습관을 찾아봐야겠습니다.
        감사합니다.

        2008.10.23 10:35 신고
    • 프로필사진

      CMyClass* pMy = NULL;

      pMy = CreateMyClass();

      CMyClass* CreateMyClass()
      {
      CMyClass* pMy = NULL;
      pMy = new CMyClass;
      return pMy;
      }

      저는 보통 이런식으로 리턴하는데 이러면 실수가
      줄어들더군요.

      2008.10.23 10:07
    • 프로필사진

      자주 하기 쉬운 실수 중 하나죠. ^^ 메모리 관련하여 또 하기 쉬운 실수 중 하나는...

      SomeType& VeryBadFunction()
      {
      SomeType st;

      // Do something with st

      return st;
      }

      void CallerFunction()
      {
      SomeType& st = VeryBadFunction();

      st.SomeMemberFunction();
      // Crash!!!
      }

      이 아닐까 합니다. ^^

      2008.10.25 16:35 신고
      • 프로필사진

        C 에는 얼핏 봐서는 실수하기 쉬운 구멍이 참 많습니다. 몇년을 작업해도 여전히 그런 구멍들이 나오는걸 보니 아직 공부가 짧은가 봐요.
        윤수님을 미2가 아닌 여기서 보니 반갑습니다 ^^;

        2008.10.25 23:04 신고
    • 프로필사진

      비밀댓글입니다

      2008.10.28 14:10
    • 프로필사진

      좋은 글 감사합니다. 함수 내에서 사용된 지역변수가 리턴과 동시에 사라지기 때문에 생기는 문제를 말씀하시는게 맞나요?

      저도 그게 궁금해서 간단한 cpp 코드를 만들어서 VS2005에서 돌려봤는데 이상하게도 DoSomething 함수 호출은 에러없이 되는데 CMyClass의 멤버 변수 접근시에는 에러가 나더라구요...

      제가 작성한 코드는 아래와 같습니다.
      #include <stdio.h>
      #include <assert.h>

      class CMyClass{

      public:
      int test;

      CMyClass(){ test = 1; };

      void DoSomething(){ printf("DOING SOMETHING";); };

      };

      void CreateMyClass(CMyClass* pMy);

      void main()
      {

      CMyClass* pMy = NULL;

      CreateMyClass(pMy);

      printf("tset = %d\n", pMy->test);// Error: Access Violation (to 0x00000000)
      pMy->DoSomething(); // No Error


      }

      void CreateMyClass(CMyClass* pMy){
      pMy = new CMyClass;
      assert(pMy);
      }

      Break point를 이용해서 DoSomthing을 호출하는 시점에서 pMy값을 조사했을때 분명히 0x00000000(NULL) 값이라서 에러가 날것으로 예상했지만 printf가 호출이 되었습니다...

      왜 그런건지 모르겠네요... 혹시 제가 놓친 부분이나 잘못 이해한게 있다면 답변 부탁드립니다.. 감사합니다~

      2008.10.29 06:52
      • 프로필사진

        안녕하세요 .
        첫번째 질문은, 지역변수로 new 한 데이터는 함수를 종료해도 없어지지 않고 댕글링 포인트(잃어버린 포인트) 가 됩니다. 파라미터로 전달한 pMy 은 여전히 NULL 이고, new 한 포인터는 공중에서 계속 떠다니게 되는 불쌍한 코드예요--;

        두번째 질문은 제가 보기엔
        pMy->DoSomething();
        에서 pMy 가 분명 NULL 이기 때문에 문제가 있지만 DoSomething 함수에서 아무런 멤버 변수에 대한 접근이 없어서 우연히도 무사히 넘어 간 것으로 보입니다. 일반적으로는 무사할 수가 없는 코드예요^^;

        2008.10.29 08:20 신고
    • 프로필사진

      오. 그렇군요. DoSomething 내부에 클래스 멤버 변수로 접근하는 명령을 줬더니 바로 Access Violation이 일어나네요. :)

      답변 감사합니다. ^^

      2008.10.29 10:30
    • 프로필사진

      오래전에 이글 봤을때 누가 이런 실수를 하냐...라고 생각했는데 방금 똑같은 실수를 했습니다..orz
      그냥 그렇다고요...;;

      2009.07.14 20:04
    • 프로필사진

      사람마다 방식이 달라서 정답은 없는 것 같은데요. 아예 참조를 넘기거나, 이중 포인터를 사용하거나, 가 쉽게 생각할 수 있는 방법으로 보입니다. 참조를 사용할 경우, 이게 값이 변경될 수 있기 때문에 저는 'const'로 넘기지 않은 참조형 parameter는 변경된다, 는 암묵적 가정 하에 코딩하고, 아예 변경될 게 아닌데 call by value로 넘기기엔 좀 크다 싶으면 const T& 처럼 꼭 const 로 넘기게 됩니다. 이에 반해 pointer 로 넘기면 반드시 값이 변경된다는 가정을 하구요. parameter 로 넘긴 값이 함수 내부에서 변경될 수 있는지를 coding style 로 알 수 있는 것이 좋다는 생각 때문에요. 음, 그래서 그런지 위처럼 값이 변경되는 경우에는... 저는 4중 포인터까지 사용해 본 적이 있어요. ^.^;;;

      2011.01.27 11:24 신고
      • 프로필사진

        네, 말씀대로, 하나라도 잘 사용하는 방법을 기본부터 확실히 깨우쳐서 사용하는 게 제일 좋을 거 같습니다.
        근데, 가끔은 남의 소스를 고치다 어쩔 수 없이 수렁이 빠지기도 해서. ㅎㅎ. 4중 포인트는 제겐 넘사벽이에요

        좋은 의견 감사합니다.

        2011.01.27 11:28 신고
Designed by black7375.