C++Builder Programming Forum
C++Builder  |  Delphi  |  FireMonkey  |  C/C++  |  Free Pascal  |  Firebird
볼랜드포럼 BorlandForum
 경고! 게시물 작성자의 사전 허락없는 메일주소 추출행위 절대 금지
C++빌더 포럼
Q & A
FAQ
팁&트릭
강좌/문서
자료실
컴포넌트/라이브러리
메신저 프로젝트
볼랜드포럼 홈
헤드라인 뉴스
IT 뉴스
공지사항
자유게시판
해피 브레이크
공동 프로젝트
구인/구직
회원 장터
건의사항
운영진 게시판
회원 메뉴
북마크
볼랜드포럼 광고 모집

C++빌더 Q&A
C++Builder Programming Q&A
[55545] Re:vector를 사용해서 클래스를 관리하는데 이상한 점이 있습니다.
sirius [] 1641 읽음    2008-12-24 02:39
이상한 일이 일어난 것은 당연합니다.
'값에 의한 복사'가 이루어 짐에도 불구하고 아래와 같이 코딩을 한다면
이상한 일이 일어나지 않는 것이 이상한 일일 겁니다..^^;
코드를 보면서 설명드리겠습니다....


이용태 님이 쓰신 글 :
: 클래스 객체를 vector에 넣어서 데이터를 관리할려고 합니다.
:
: 밑에 소스에서 보는것과 같이..
:
: 지역 클래스 객체 2개를 만든다음에 벡터에 넣고... 메모리를 할당하고 데이터 값을 초기화 합니다.
:
: 그런데 이상한게.. 각 객체에 대해서 따로따로 동적으로 메모리를 할당했는데..
:
: 동적으로 생성된 메모리 주소를 비교해보면 두개의 객체가 한개의 동일한 메모리를 가리킵니다.
:
: 벡터에 어떤 값을 집어 넣을경우에 값에 의한 복사가 이루어지는것으로 알고 있는데요... 서로 다른 객체 2개를 넣으면 서로 다른 복사본 객체 2개가 벡터에 넣어져야 하고 벡터에 들어가 있는 클래스의 멤버포인터 변수에 동적으로 메모리 할당하면 2개의 할당된 메모리가 있어야 하는거 아닌가요?
:
: 왜 그런지 모르겠습니다. 먼가 제가 놓치고 있는 부분이 있는거 같은데요...
:
: 어떤게 틀렸는지 알려주세요...
:
:
: -------------------------------------------------------------------------------------
: 클래스는 다음과 같구요..
:
: class TDataHandling_ECT
: {
:     private :
:         short *pHori;
:         int m_nSize;
:
:     public :
:         TDataHandling_ECT()
:         {
:             pHori = NULL;
:             m_nSize = 0;
:         }
:
:         ~TDataHandling_ECT()
:         {
:             delete [] pHori;
:         }
:
:         void CreateData(int nSize)
:         {
:             m_nSize = nSize;
:
:             pHori = new short[nSize];
:         }
:
:         short* GetHori()
:         {
:             return pHori;
:         }
: };
:
:
: void __fastcall TForm1::Button1Click(TObject *Sender)
: {
:     vEct_Data.clear();
:
:     TDataHandling_ECT var;
>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
현 시점에서는 stack에 var이 생성되었습니다.
    var.pHori = NULL
인 상황입니다. 생성자가 자동으로 불려 초기화 되었을 것이니까요...

:
:     vEct_Data.push_back(var);
>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
var.pHori 는 vector로 들어가면서 vector내부의 저장 창고로 한차례 복사가 이루어 집니다.
(컴파일러가 친절하게 만들어 준 복사 생성자가 한번 호출 되겠죠?)
지금 시점에서는
    var.pHori = NULL
은 그대로며, 또한
    vector[0].pHori = NULL
인 상황입니다.(그대로 복사가 이루어 졌을 것이므로...)

:
:     vEct_Data[0].CreateData(50);
:     //(vEct_Data.back()).CreateData(50);
>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
이제 동적으로 memory 할당을 하게 됩니다.
[] operator는 reference를 반환하는게 정상이므로 여기서는 복사는 이루어 지지 않으며
vector내의 저장 창고에 있는 객체의 pHori에 동적 할당이 되게 됩니다.
    var.pHori = NULL
    vector[0].pHori = somewhere on heap
인 상황이 만들어 졌습니다.

:
:     for(int i = 0; i < 50; i++)
:         (vEct_Data.back()).GetHori()[i] = i;
:
:
:     TDataHandling_ECT var1;
>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
여기도 stack에 var1의 객체가 자리를 잡겠네요...
위와같이
    var1.pHori = NULL
입니다.

:
:     vEct_Data.push_back(var1);
>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
여기서 어마어마한 일이 발생하게 됩니다.
vector에 var1의 '복사체'를 추가하기 위해 기존에 있던 vector의 저장 창고를 늘려야 합니다.
(vector의 구현이 어떻게 이루어 져 있느냐에 따라 다르겠지만 보통 이런 일이 발생하게 되겠지요....)

vector는 조금 더 큰 새로운 창고를 만들고 기존에 가지고 있던 var의 '복사체', 즉 vector[0]을
새로운 저장창고로 복사해 넣습니다.(복사 생성자가 한번 호출되겠죠?)
그 후 기존 창고에 있던 복사체를 제거합니다.(소멸자가 한번 호출됩니다.)
네.... 느낌이 파파박;; 오죠?
복사 생성자로 인해
    새 복사체.pHori = 헌 복사체.pHori
가 되면서 복사가 된 후 헌 복사체는 소멸됩니다. 이때 불리는 소멸자에는
떡하니 delete operator가 들어가 있네요...
헌 복사체.pHori 가 가리키고 있던 somewhere on heap 이라는 메모리는
저 멀리 사라져버립니다.... 단지 주소만 가리키고 있을 뿐 확보했던 메모리는 저 세상으로 가버린 것이죠...
다시 말해 똑같은 memory 주소를 가리키고 있는
새 복사체.pHori 는 이제 저 세상으로 가버린 메모리를 pointing하고 있을 뿐입니다.
이제부터 이 application은 예측할 수 없는 행동을 하기 시작합니다.
(해당 memory 접근 시 오류를 내고 application이 뻗는 것이 정상인데... 간혹 알수 없는
다른 행동들을 하기도 하더군요...-_-
이 상황에서 다른 component들을 폼에 추가한다든지 하여 컴파일을 해서 실행해 보면
아래의 if 구분이 false가 되는 경우도 생깁니다.
즉 이제부터 어찌 될지는 아무도 알 수 없습니다..-_-;;)

:
:     vEct_Data[1].CreateData(50);
:     //(vEct_Data.back()).CreateData(50);
:
:     if(vEct_Data[0].GetHori() == vEct_Data[1].GetHori())
:         ShowMessage("Same");
>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
여기서 함수 stack이 종료됩니다.
var, var1, vector[0], vector[1]의 모든 소멸자가 호출됩니다.
    var.pHori = NULL
    var1.pHori = NULL
먼저 요넘 둘이 delete 되는 것을 보면....컴파일러에 따라 NULL을 delete할 경우 뻗는 경우도 있습니다.
    vector[0].pHori = 이미 저세상 가버린 somewhere on heap
여기선 왠만하면 뻗어야 합니다..-_-;;; 안뻗는 경우도 있긴 하던데...
나중에 프로그램이 덩치가 커 진후 뻗기 시작하면 디버깅시 머리에 쥐를 내리게 하는 주요 뽀인트가 될 수 있겠죠....
    vector[1].pHori = somewhere on heap
요넘만 정상적으로 delete가 될 수 있겠네요....

: }
>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
이처럼 객체를 복사할 경우에는 조심 또 조심해야 합니다.
특히 그 객체 내부에 메모리를 동적 할당 하는 루틴이 있을 경우는 더 하구요...
이런 경우 class설계를 좀더 주의깊게 해야할 듯 싶습니다.

전 개인적으로 객체는 복사가 안되도록 복사 생성자를 private으로 선언해 버리고
객체의 pointer를 가지고 노는 방법을 즐겨 씁니다...^^;
(머..꼭 위의 문제 때문만은 아니고...
vector를 내가 만든것도 아니니.. 내부적으로 복사를 얼마나 해댈지 모르기때매...
비싼 복사 비용을 지불하고 싶지 않거든요..ㅋㅋ
원래 C를 하던 사람이라 그런지 객체 한번 복사될 때마다 가슴이 벌렁벌렁 합니다..ㅎㅎㅎ)

만족할 만한 답이 되었나 모르겠네요.. 그럼 즐프하세요~~~

+ -

관련 글 리스트
55533 vector를 사용해서 클래스를 관리하는데 이상한 점이 있습니다. 이용태 1097 2008/12/23
55545     Re:vector를 사용해서 클래스를 관리하는데 이상한 점이 있습니다. sirius 1641 2008/12/24
55535     Re:vector를 사용해서 클래스를 관리하는데 이상한 점이 있습니다. 장성호 1026 2008/12/23
55536         Re:Re:vector를 사용해서 클래스를 관리하는데 이상한 점이 있습니다. 이용태 907 2008/12/23
55537             Re:Re:Re:vector를 사용해서 클래스를 관리하는데 이상한 점이 있습니다. 장성호 1126 2008/12/23
55539                 Re:Re:Re:Re:vector를 사용해서 클래스를 관리하는데 이상한 점이 있습니다. 이용태 1088 2008/12/23
Google
Copyright © 1999-2015, borlandforum.com. All right reserved.