C++ 컨테이너 선택에 대해 여쭙고자 합니다.
제가 어떠한 클래스에서 쓰고 싶은 컨테이너는 특정 객체를 10개만 저장하는 컨테이너입니다.
그런데, 인덱스가 1~10까지 고정되어 있었으면 좋겠습니다.
그래서 <인덱스, 객체> 방식으로 map을 생각했는데요.
map을 쓰려니 좀 난감한 점이 있더라구요.
이 컨테이너에서 null인 객체가 몇 개인지를 찾는지에 대한 로직을 구현한다고 보면
그냥 단순히 생각했을 땐 (m->find(i) == m->end)인지 아닌지를 봐야되는데 이걸
i를 1부터 10까지 돌릴려고 보니 좀 짜증나더라구요.
선형적 비용 + 로그 비용(find 함수)가 드니까 비효율적으로 보이기도 하고...
반대로 map이 아니라 벡터라면 아래와 같이 하면 되서 편하구요.
****단, 벡터에 erase나 remove를 쓰는게 아닌 해당 인덱스값에 널값을 넣는 방식으로 삭제를 했습니다.****
(이 방법이 문제를 일으킬까요?)
for(Obj obj: *objVector) { if(obj == 0) ++numOfEmpty; }
컨테이너 안에 객체를 삽입/삭제 하고싶을때도 일단 해당 인덱스 안에 객체가 있는지 판별을 해야 하는데
map의 find 함수보단 직관적으로 objVector->at()을 이용하는게 편하구요(물론 벡터에서의 element 삭제 함수를 쓰는게 아닌 널 값으로 삽입한다는 가정하에)
사실 인덱스 범위가 고정되어야 하고 요소 삽입이나 삭제시에 요소들이 안밀려나게 할려면 벡터는 부적합하고 맵이 훨씬 적합하긴 한데...
이런 현실적인 문제때문에 일단은 벡터를 쓰고 있습니다. 잘 돌아는 가구요.
그런데, 제가 원하는 바를 map으로 효과적으로 구현을 할 수 있을까요?
조금 더 질문을 함축하면 map에서 null인 객체를 "map->find(index) == map->end()" 방식 말고 다르게 찾을 수 있을까요?
그러면 벡터 대신 map을 쓸 수 있을거 같습니다.
아시는 분 있으시면 도와주세요 ㅎㅎ~~
왜 map이 더 적합한가요?
왜 map이 더 적합한가요?
제가 생각하는 이유는 다음과 같아요~
벡터를 썼을 때 삭제 연산을 일반적인 erase를 써서 지우고 싶은데 이렇게 되면 인덱스가 제가 생각하는 대로 유지가 안됩니다.
즉, 5번째의 객체가 삭제되었으면 여긴 삭제된채로 남아있어야 하는데(5번 인덱스는 null로) erase를 하면 그 뒤의 원소들이 한칸씩 당겨지니깐요...
그래서 궁여지책으로 삭제 연산을 erase를 쓰지 않고 null값을 넣은거였구요.
뭐 이런 방법이 문제 될게 없다면 저도 벡터가 적합하다고 생각해요 ㅎㅎ
이 컨테이너에서 null인 객체가 몇 개인지를
컨테이너에서 null인 객체의 개수 = 10 - map.size() 하면 되지 않을까요?
——
———
Life is a tragedy when seen in close-up, but a comedy in long-shot. - Chaplin, Charlie -
전체 NULL의 갯수는 뭐 그렇게 구할수는 있겠지만..
갯수만 구하는게 아니라 하나하나 NULL인지 따져볼려면 어차피 FIND를 해야되는건 마찬가지라서요ㅠ
혹시 iterator는 알고 계신가요? 모든 요소를
혹시 iterator는 알고 계신가요? 모든 요소를 탐색하는데 왜 find를 쓰시는지 모르겠습니다.
아 제 말은...
map에서 erase를 해버리면 이터레이터를 써서 접근을 하면 해당 객체가 null인지 아닌지 제대로 판별을 못하는거 같아요...
소스 한번 봐주세요~
제가 NULL 값 판별하는 코드를 잘못 짰는지...
한번 봐주세요~
반복자를 통해서 접근하면
1번 3번 4번 5번 이렇게 밖에 접근을 못하고 2번에 접근을 못하게 되네요...
인덱스 정보를 유지하려면 vector이던 map이던 erase를 쓰지말고 null값으로 덮어씌워야 되는듯...
왜 굳이 index 정보를 유지하려고
왜 굳이 index 정보를 유지하려고 하시는건가요?
10개를 유지하는것이 중요하다면 그냥 *Object[10] 을 사용하시는게 나아보이는데요.
입력을 받을 때
인덱스를 1~10까지 고정적으로만 받을거라서요~
말씀하신대로 그냥 배열을 쓸까했는데...
코드에 반복자가 들어가는 곳이 너무 많아서 GG네요 ㅋㅋ
자주 불리는 부분을 함수로 만들면되지요.
자주 불리는 부분을 함수로 만들면되지요.
무슨 말씀인지 잘 이해가 가질 않아요ㅠ
코드로 설명을 해주실 수 있을까요?
왜 굳이 연산자를 operator[]로 호출하나요?
왜 굳이 연산자를 operator[]로 호출하나요? 그냥 m[1] = human1; 처럼 하면 되는데...
그리고 당연히 지워버리면 NULL도 뭐도 아닙니다. 지우면 그냥 map 안에 없는겁니다. erase가 호출되고 나면 map의 크기 자체가 하나 줄어듭니다.
map을 안 쓰시는 게 좋을 것 같은데요
map은 대량의 데이터를 넣었을 때 효율적으로 검색을 할 수 있어야 하기 때문에 보통 balanced tree 구조를 하고 있습니다.
이 balanced tree가 말은 좋은데 사실 오버헤드가 큽니다. 데이터를 하나 집어넣을 때마다 루트에서부터 탐색을 하며 맞는 자리를 찾고 필요에 따라 트리를 회전시키는 삽질을 해야 합니다.
데이터 열 개짜리 map을 사용하면 뭐 동작이야 하겠지만 뭘 해도 vector보다 느릴 거라는 데 한표 던집니다.
다음과 같이 하면 될 듯요
(1) 객체 대신 객체 포인터를 넣은 선언,
(2) 객체를 지워야할 때에는 erase 대신 지울 객체의 포인터를 delete 하고, 해당 map 의 위치를 NULL로 설정
Human* 대신 tr1::shared_ptr 을 사용하신 다면 삭제하는게 좀더 간단해지고, 메모리 누수에 대한 염려도 줄겠죠.
STL에 어느 정도 익숙해 지고, 어떤 컨테이너를
STL에 어느 정도 익숙해 지고, 어떤 컨테이너를 어떤 알고리듬을 써야 하는가가 슬슬 궁금해진다면,
Effective STL을 읽어야 할 때가 된 듯 싶습니다.
댓글 달기