어떤 시나리오에서 특정 STL 컨테이너를 사용합니까?


185

C ++에 대한 책에서 STL 컨테이너, 특히 STL 및 컨테이너에 대한 섹션을 읽었습니다. 이제 나는 각자의 고유 한 속성을 가지고 있으며 모든 것을 암기하는 것에 가깝습니다 ... 그러나 아직 이해하지 못하는 것은 각 시나리오가 사용되는 시나리오입니다.

설명은 무엇입니까? 예제 코드가 훨씬 선호됩니다.


지도, vectot, 세트 등을 의미합니까?
Thomas Tempelmann

이 다이어그램을 보더라도 내 quastion에 가장 적합한 것이 무엇인지 말할 수 없습니다 stackoverflow.com/questions/9329011/…
sergiol

2
@ sbi :이 태그에서 C ++ Faq 태그를 제거하고 최신 및 C ++ 11에 추가하십시오 .C ++ 11 에서 표준 라이브러리 컨테이너를 어떻게 효율적으로 선택할 수 있습니까?
Alok Save

답변:


338

이 치트 시트 는 다른 컨테이너에 대한 요약을 제공합니다.

다른 사용 시나리오에서 사용하기위한 가이드로 하단의 플로우 차트를 참조하십시오.

http://linuxsoftware.co.nz/containerchoice.png

만든 데이비드 무어BY-SA 3.0 CC 라이센스


14
이 플로차트는 황금색입니다. 저는 C #에서 이와 같은 것을 원했습니다
Bruno

2
업데이트 된 링크 : C ++ 컨테이너 치트 시트 .
Bill Door

3
시작 지점은 vector비어 있어야합니다 . stackoverflow.com/questions/10699265/…
eonil

5
이제이 unordered_mapunordered_set(과 멀티 변종) 흐름도에없는하지만 당신은 순서 만 키로 요소를 찾을 필요가 걱정하지 않을 때 좋은 종목이다. 조회는 일반적으로 O (log n) 대신 O (1)입니다.
Aidiakapi

2
@ shuttle87은 그 크기뿐만 아니라 크기도 컴파일 타임에 결정되며 결코 변하지 않을 것입니다.
YoungJohn

188

다음은 내가 만든 David Moore의 버전 (위 참조)에서 영감을 얻은 순서도이며 새로운 표준 (C ++ 11)으로 최신 상태입니다. 이것은 내 개인적인 취향이며 논쟁의 여지가 없지만이 토론에 가치가 있다고 생각했습니다.

여기에 이미지 설명을 입력하십시오


4
원본을 사용할 수 있습니까? 훌륭한 차트입니다. 블로그 나 GitHub에 붙어 있을까요?
kevinarpe

1
이것은 훌륭한 차트입니다. 누군가가 '지속적인 입장'의 의미를 설명해 줄 수 있습니까?
IDDQD

3
@STALKER Persistent position은 컨테이너의 요소에 대한 포인터 또는 반복자가있는 경우 컨테이너에서 추가하거나 제거하는 내용에 관계없이 포인터 또는 반복자가 컨테이너에 추가하거나 제거하는 것과 상관없이 유효한 상태를 유지하고 동일한 요소를 가리키는 것을 의미합니다 문제의 요소가 아닙니다).
Mikael Persson

1
이것은 정말 훌륭한 차트이지만 vector (sorted)나머지 부분과 약간 일치하지 않는다고 생각 합니다. 다른 유형의 컨테이너가 아니라 동일 std::vector하지만 정렬되었습니다. 더 중요한 것은, 이것이 std::set트로프 세트를 반복하는 표준 동작 인 경우 왜 순서 반복에 대해을 사용할 수 없는지 알 수 없습니다 . 물론, 대답이 컨테이너 트로프의 값에 순서대로 액세스하는 것에 대해 이야기하고 있다면 []괜찮습니다 std::vector. 그러나 두 경우 모두 "주문이 필요합니다"라는 질문 직후에 결정해야합니다.
RAs

1
@ user2019840 차트를 표준 컨테이너로 제한하고 싶습니다. "정렬 된 벡터"대신에 나타나는 것은 "flat_set"( Boost.Container ) 또는 이에 상응하는 것입니다 (모든 주요 라이브러리 또는 코드베이스에는 flat_set에 상응하는 AFAIK가 있습니다). 그러나 이것들은 비표준이며 STL에서 눈에 띄지 않습니다. 그리고 std :: set 또는 std :: map (적어도 자주는 아님)을 반복하지 않으려는 이유는 그렇게하는 것이 매우 비효율적이기 때문 입니다.
Mikael Persson

41

간단한 대답 : std::vector달리 할 특별한 이유가 없다면 모든 것을 사용하십시오 .

"Gee, std::vectorX 때문에 여기에서 잘 작동하지 않습니다 "라고 생각하는 경우를 찾으면 X를 기준으로하십시오.


1
그러나 .. 반복 할 때 항목을 삭제 / 삽입하지 않도록주의하십시오 ... const_iterator를 최대한 사용하여이 문제를 피하십시오.
vrdhn

11
흠 ... 사람들이 벡터를 과도하게 사용하고 있다고 생각합니다. 그 이유는 "작동하지 않는"사례가 쉽게 발생하지 않기 때문입니다. 따라서 사람들은 가장 자주 사용되는 컨테이너를 고수하고 목록, 대기열 등을 저장하기 위해 잘못 사용합니다. "모두 맞는 것"을 적용하는 대신 의도 된 용도에 따라 용기를 선택해야합니다.
Black

13
@Black Point는 이론 상으로는 느리게 작동해야하는 작업에서도 벡터가 더 빠릅니다.
Bartek Banachewicz

1
@Vardhan std::remove_if은 "반복 중 삭제"접근 방식보다 거의 항상 우수합니다.
fredoverflow

1
일부 벤치 마크는이 논의가 주관적이지 않도록하는 데 실제로 도움이됩니다.
Felix D.

11

Scott Meyers의 Effective STL을보십시오. STL 사용법을 잘 설명합니다.

결정되거나 결정되지 않은 수의 객체를 저장하고 절대 삭제하지 않으려는 경우 벡터가 원하는 것입니다. C 배열의 기본 대체이며 하나처럼 작동하지만 오버플로하지는 않습니다. reserve ()를 사용하여 크기를 미리 설정할 수 있습니다.

결정되지 않은 수의 객체를 저장하고 싶지만 객체를 추가하고 삭제하려는 경우 벡터와 달리 다음 요소를 이동하지 않고 요소를 삭제할 수 있기 때문에 목록을 원할 것입니다. 그러나 벡터보다 많은 메모리가 필요하며 요소에 순차적으로 액세스 할 수 없습니다.

많은 요소를 가져 와서 해당 요소의 고유 한 값만 찾으려면 모두 한 세트로 읽으면 정렬되어 정렬됩니다.

키-값 쌍이 많고 키별로 정렬하려면 맵이 유용하지만 키당 하나의 값만 보유합니다. 키당 하나 이상의 값이 필요한 경우 맵에서 값으로 벡터 / 목록을 만들거나 멀티 맵을 사용할 수 있습니다.

STL에는 없지만 STL에 대한 TR1 업데이트에 있습니다. 키별로 조회 할 키-값 쌍이 많고 순서에 신경 쓰지 않으면 tr1 :: unordered_map 인 해시를 사용하고 싶습니다. stdext :: hash_map이라는 Visual C ++ 7.1과 함께 사용했습니다. 맵에 대한 O (log n) 조회 대신 O (1) 조회가 있습니다.


나는 Microsoft의 hash_map구현이 좋지 않다는 몇 가지 일화를 들었습니다 . 나는 그들이 더 잘했으면 좋겠다 unordered_map.
Mark Ransom 2016 년

3
목록 중- "순차적으로 요소에 액세스 할 수 없습니다." -요소에 직접 액세스하거나 색인을 생성 할 수 없다는 뜻입니다 ....
Tony Delroy

^ 그렇습니다. 순차적 액세스는 정확히 무엇을하기 때문 list입니다. 오히려 눈부신 오류가 있습니다.
underscore_d

7

3 가지 속성을 갖도록 플로우 차트를 재 설계했습니다.

  1. STL 컨테이너는 두 가지 주요 클래스로 분류됩니다. 기본 컨테이너와 해당 컨테이너는 기본 컨테이너를 활용하여 정책을 구현합니다.
  2. 처음에 순서도는 의사 결정 프로세스를 결정해야 할 주요 상황으로 나누고 각 사례에 대해 자세히 설명해야합니다.
  3. 일부 확장 컨테이너는 내부 컨테이너로 다른 기본 컨테이너를 선택할 수 있습니다. 순서도는 각각의 기본 컨테이너가 사용될 수있는 상황을 고려해야합니다.

순서도 : 여기에 이미지 설명을 입력하십시오

이 링크 에 자세한 정보가 제공됩니다 .


5

간략하게 만 지금까지 언급 한 중요한 점은, (C 배열은 제공 등)이 연속 메모리를 필요로하는 경우에, 당신 만 사용할 수 있다는 것입니다 vector, array또는 string.

array컴파일 타임에 크기를 알고있는 경우 사용하십시오 .

string문자 컨테이너로만 작업하고 범용 컨테이너가 아닌 문자열이 필요한 경우에 사용하십시오 .

vector다른 모든 경우에 사용하십시오 ( vector대부분의 경우 컨테이너의 기본 선택이어야 함).

이 세 가지를 모두 사용하면 data()멤버 함수를 사용 하여 컨테이너의 첫 번째 요소에 대한 포인터를 얻을 수 있습니다 .


3

모두 저장하려는 내용과 컨테이너로 수행하려는 내용에 따라 다릅니다. 가장 많이 사용하는 컨테이너 클래스에 대한 몇 가지 (매우 철저하지 않은) 예제는 다음과 같습니다.

vector: 포함 된 객체 당 메모리 오버 헤드가 거의 또는 전혀없는 컴팩트 한 레이아웃. 반복하기에 효율적입니다. 추가, 삽입 및 지우기는 특히 복잡한 개체의 경우 비용이 많이들 수 있습니다. 색인으로 포함 된 객체를 찾는 것이 저렴합니다 (예 : myVector [10]). C에서 배열을 사용한 위치를 사용하십시오. 간단한 객체가 많은 곳 (예 : int)에 좋습니다. reserve()컨테이너에 많은 객체를 추가하기 전에 사용하는 것을 잊지 마십시오 .

list: 포함 된 객체 당 작은 메모리 오버 헤드. 반복하기에 효율적입니다. 추가, 삽입 및 삭제가 저렴합니다. C에서 링크 된 목록을 사용한 위치를 사용하십시오.

set(및 multiset) : 포함 된 객체 당 상당한 메모리 오버 헤드. 컨테이너에 지정된 객체가 포함되어 있는지 빠르게 확인하거나 컨테이너를 효율적으로 병합해야하는 위치에서 사용하십시오.

map(및 multimap) : 포함 된 객체 당 상당한 메모리 오버 헤드. 키-값 쌍을 저장하려는 위치를 사용하고 키별로 값을 빠르게 검색하십시오.

온 흐름도 컨닝 페이퍼 zdan 제안은보다 포괄적 인 가이드를 제공합니다.


"포함 된 개체 당 작은 메모리 오버 헤드"는 목록에 해당되지 않습니다. std :: list는 이중 연결 목록으로 구현되므로 무시하지 않는 저장된 객체 당 2 개의 포인터를 유지합니다.
한나 칼릴

저장된 객체 당 두 개의 포인터를 "작은"것으로 계산합니다.
입찰 :

무엇에 비해? std :: forward_list는 주로 객체 당 메타 데이터를 적게 저장하도록 제안 된 컨테이너입니다 (하나의 포인터 만). std :: vector는 객체 당 0 개의 메타 데이터를 보유합니다. 따라서 다른 컨테이너에 비해 2 개의 포인터를 협상 할 수 없습니다
Hanna Khalil

그것은 모두 물체의 크기에 달려 있습니다. 이미 벡터에 "포함 된 객체 당 메모리 오버 헤드가 거의 또는 전혀없는 컴팩트 한 레이아웃"이 있다고 말했습니다. 여전히 list는 set 및 map에 비해 메모리 오버 헤드가 작고 벡터보다 약간 더 큰 메모리 오버 헤드가 있다고 말합니다. TBH를 만들려는 시점이 무엇인지 잘 모르겠습니다.
입찰

모든 모드 기반 컨테이너는 동적 할당으로 인해 상당한 오버 헤드가 발생하는 경향이 있으며 거의 ​​무료입니다. 물론 사용자 지정 할당자를 사용하지 않는 한.
MikeMB

2

내가 배운 한 가지 교훈은 다음과 같습니다. 컨테이너 유형을 좋은 날로 변경하면 큰 놀라움을 줄 수 있기 때문에 클래스로 포장하십시오.

class CollectionOfFoo {
    Collection<Foo*> foos;
    .. delegate methods specifically 
}

초기 비용이 많이 들지 않으며 누군가이 구조에서 x 작업을 수행 할 때마다 중단하고 싶을 때 디버깅 시간을 절약합니다.

작업을위한 완벽한 데이터 구조를 선택합니다.

각 데이터 구조는 몇 가지 작업을 제공하며 시간이 다양 할 수 있습니다.

O (1), O (lg N), O (N) 등

기본적으로 어떤 연산이 가장 많이 수행되는지 가장 잘 추측해야하며 해당 연산이 O (1) 인 데이터 구조를 사용해야합니다.

간단하지 않습니까 (-:


5
이것이 반복자를 사용하는 이유가 아닙니까?
Platinum Azure

@PlatinumAzure 반복자조차 멤버 typedef가되어야합니다. 컨테이너 유형을 변경하면 모든 반복자 정의도 변경해야합니다 ... c ++ 1x로 수정되었습니다!
vrdhn

4
궁금한 점은 C ++ 11의 수정 사항입니다. auto myIterator = whateverCollection.begin(); // <-- immune to changes of container type
Black

1
A는시겠습니까 typedef Collection<Foo*> CollectionOfFoo;충분?
Craig McQueen

5
나중에 마음을 바꾸고 다른 컨테이너에 위임 할 수는 없을 것 입니다. 컨테이너 독립적 코드의 환상을 조심하십시오
fredoverflow


1

나는이 질문의 dup으로 표시된 다른 질문에 이것을 대답했습니다. 그러나 표준 컨테이너를 선택하기로 한 결정에 관한 좋은 기사를 참조하는 것이 좋습니다.

@David Thornley가 대답했듯이 std :: vector는 다른 특별한 요구가 없다면 갈 길입니다. 이것은 C ++ 개발자 인 Bjarne Stroustrup이 2014 년 블로그에서 제공 한 조언입니다.

기사 https://isocpp.org/blog/2014/06/stroustrup-lists에 대한 링크는 다음과 같습니다.

그 말에서 인용하면

그리고 예, 기본적으로 std :: vector를 사용하는 것이 좋습니다.

의견에서 @NathanOliver 사용자는 더 구체적인 측정을 제공하는 또 다른 좋은 블로그를 제공합니다. https://baptiste-wicht.com/posts/2012/12/cpp-benchmark-vector-list-deque.html .

당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.