관리 메뉴

有希

std::map(set) insert 정확하게 사용하기 본문

프로그래밍/C++

std::map(set) insert 정확하게 사용하기

有希. 2022. 8. 7. 20:36

출처: https://yonmy.com/archives/9

 

YonMy » std::map(set) insert 정확하게 사용하기

꽤나 많은 프로그래머들이 std::map insert 의 사용을 잘 못 하고 있는 경우가 있어서, 오늘은 그것에 대해 이야기 하려고 한다. 우선 std::map의 insert의 기본형을 보면 다음과 같다. pair<iterator,bool> inser

yonmy.com


나나 다른 사람들의 코드를 보면 대부분 이렇게 쓰고 있다.

std::map<int, std::string> temp;
auto itr = temp.find(1);
if (itr == temp.end())
{
    temp.insert({ 1, "number 1" });
}
else
{
    itr->second = "number 1";
}

1. 우선 find
2. 찾지 못했다면 insert 찾았다면 값 change

이런 로직으로 된다. 하지만 위 표를 보면 리턴값으로 pair를 돌려주는 것을 알 수 있다.

즉, 위의 코드는 아래와 같이 바뀌어야 한다.

std::map<int, std::string> temp;
auto ret = temp.insert({ 1, "number 1" });
if (!ret.second)
{
    ret.first->second = "number 1";
}

찾는 것이 아닌 어차피 insert 할 것이라면 처음부터 insert를 한다. 그리고 성공했으면 ret.second만 보면 되고 아니라면 그 때 값을 바꾸면 된다.
위와 차이점은 temp.insert 분기를 탔을 경우 find비용만큼 차이가 나게 된다는 점이다.

map에 클래스를 넣을 때, 자료가 매우 많아 메모리를 할당하는 것이 성공할 지 실패할 지 알 수 없을 때 다음과 같이 우선 두드려 보고 넣는 식으로 코드를 짜게 된다.

std::map<int, Foo*> temp;
temp.insert({ 1, new Foo });
 
...
 
{// 이렇게 하는 것은 중복된 Key를 삽입하는 경우가 많아지면, 무의미하게 new를 했다가 delete를 하는 부담이 생긴다.
    Foo* pFoo = new Foo;
    auto ret = temp.insert({ 1, pFoo });
    if (!ret.second)
    { 
        delete pFoo;
    }
}
 
{// 그래서 다음과 같이 잘못 사용 하게 된다.
    auto itr = temp.find(1);
    if (itr == temp.end() )
    {
        temp.insert({ 1, new Foo });
    }
}

2가지 방식을 해보는데,
1번 째는 docs를 참고한 우선 넣어보고 반환받은 pair를 이용해서 실패(중복)이라면 클래스를 delete 하게 된다.
2번 째는 우선 find하고 없으면 넣는다.

2번 째 방식이 1번 째 보단 나을 수 있으나 결국 가장 위에서 봤듯이 find, insert 비용을 중복으로 소모한다.
애초에 insert할 것이라면 find 할 필요 없이 아래처럼 사용한다.

nullptr를 넣고 키 값만 확인한 후에 true이면 그 때 할당해주면 된다.

auto ret = temp.insert({ 1, nullptr });
if (ret.second)
{
    ret.first->second = new Foo;
}

'프로그래밍 > C++' 카테고리의 다른 글

C++/STL/Vector 동작 원리  (0) 2022.01.31
C++/캐스팅(Casting)  (0) 2022.01.29
C++/클래스 사이의 타입 변환  (0) 2022.01.29
C++/malloc(free) vs new(delete)  (0) 2022.01.28
C++/메모리 구조  (0) 2022.01.28