2011-02-07 4 views
3

내 게임 엔진 프로젝트에서는 주로 std::stringstd::vector 클래스의 STL을 광범위하게 사용합니다.C++ : STL 컨테이너를 반복 할 수있는 적절한 방법

대부분의 경우 반복해야합니다. 지금 내가하고 있어요 방법은 다음과 같습니다

for(unsigned int i = 0; i < theContainer.size(); i ++) 
{ 

} 
  • 오전 나는 그것을 올바른 방법으로 일을?
  • 그렇지 않은 경우 왜, 대신 무엇을해야합니까?

  • size()는이 구현에서 루프주기마다 실제로 실행됩니까? 성능 손실은 무시할만한 수준입니까?

+0

'unsigned int'대신 'size_t'를 사용해야합니다. – Maxpm

+3

@Maxpm - 또는 더 나은 것은':: std :: vector :: size_type'입니다. – Omnifarious

+1

begin() 및 end()는 O (1)의 복잡성을 보장합니다. 크기는 일반 컨테이너에 대해서만 O (n)의 보증을 갖지만 (문자열과 벡터는 일반에 비해 추가 보증을 할 수 있음) –

답변

1

일반적으로 컨테이너를 "반복"하는 올바른 방법은 "반복기"를 사용하는 것입니다. 각 요소를 수정하지 않을 경우 string::const_iterator를 사용하는 물론

string myStr = "hello"; 
for(string::iterator i = myStr.begin(); i != myStr.end(); ++i){ 
    cout << "Current character: " << *i << endl; 
} 

같은 뭔가, 그것은 가장 좋습니다.

그리고 네, size()는 때마다 호출되는, 많은 경우에 성능 손실이 눈에 띄는 것하고 (1), 그러나 이전에 크기를 계산하는 것이 좋습니다의 O 그래서 그것은, O (N)입니다 매번 호출하는 크기보다 루프.

+2

더 많은'const_iterator' 필요 : – genpfault

+5

나는 동의하지 않는다. 크기()의 복잡성은 일정합니다. http://www.cplusplus.com/reference/stl/vector/size/ –

+0

글쎄, 일정해야한다. braindead 구현에서만 상수가 아니지만 컨테이너의 'size()'멤버 함수에 대한 복잡성 요구 사항이 없습니다 (예 : libstdC++의 일부 std :: list와 같은 일부 컨테이너의 일부 구현은 이). C++ 0x는'size()'를 지원하는 컨테이너가 일정한 시간 복잡성을 가지고 그렇게해야한다는 요구 조건을 추가합니다. –

4

STL 컨테이너 반복자

vector<int> v; 
for (vector<int>::iterator it = v.begin(); it!=v.end(); ++it) { 
    cout << *it << endl; 
} 

크기()를 반복 할 때마다 재 계산 될 것이다 지원한다.

+0

그리고'v.end()'는 어떨까요? – Omnifarious

+0

v.end()는 일반적으로 최적화되었지만 업데이트됩니다. iterator를 반복하면서 컨테이너를 수정하는 것은 컨테이너에서 수행중인 정확한 작업에 따라 까다로운 비즈니스입니다. 예를 들어, 컨테이너에 대한 erase 호출은 반복자를 무효화하고 erase에 의해 반환 된 반복자를 사용해야합니다. 여기에 몇 가지 추가 정보가 있습니다 : http://bytes.com/topic/c/answers/603065-good-practice-modifying-vector-while-iterating-through –

+2

end()와 size()가 모두 최적화되도록 최적화되어 있습니다. 작거나 0 일. –

2
  • 랜덤 액세스 컨테이너의 경우 잘못된 것은 아닙니다.
  • 하지만 반복기를 대신 사용할 수 있습니다.

    for (string::const_iterator it = theContainer.begin(); 
        it != theContainer.end(); ++it) { 
        // do something with *it 
    } 
    
  • 컴파일러가 통화 (반복자 경우 또는 .end())를 .size()을 최적화 할 수있는 어떤 상황하에있다 (예를 들어 만 const 접속, 함수 pure이다). 그러나 그것에 의존하지 마십시오.

+0

이것은 약간 잘못된 것입니다. 'size' 또는'end'에 대한 호출은 항상 최적화됩니다. 변수 조회는 수행되지 않습니다. 따라서 우리는 더 이상 호출의 오버 헤드를 처리 할 필요가 없지만 여전히 메모리를 찾아야합니다. –

4

표준 알고리즘을 살펴볼 수 있습니다. 예를 들어

vector<mylass> myvec; 

// some code where you add elements to your vector 

for_each(myvec.begin(), myvec.end(), do_something_with_a_vector_element); 

do_something_with_a_vector_element는 루프 예를 들어

에가는 것을 수행하는 기능입니다

(가) 표준 알고리즘 많이 있습니다
void 
do_something_with_a_vector_element(const myclass& element) 
{ 
// I use my element here 
} 

-http://www.cplusplus.com/reference/algorithm/를 참조 - 그래서 대부분의 모든 것이 지원됩니다.

+1

'for_each'는 적용 할 함수 또는 펑터가 필요합니다. C++ 0x lambdas로 훨씬 더 좋을 것입니다. –

+0

+1 for_each가 올바른 사용법이며 David이 말했듯이 C++ 0x와 lambdas를 사용할 때만 더 달콤 해집니다. –

+1

함수 포인터 대신 펑터 객체를 사용하는 것이 좋습니다. 전자는 컴파일러에 의해 인라인 될 가능성이 높기 때문입니다. (다른 이점도 있습니다.) – ephemient

0

벡터에 대해서는 잘하고 있습니다 만 다른 컨테이너에서는 올바른 방법으로 변환되지 않습니다.

더 일반적인 방법은 내가 정말 좋아하는 것보다 더 입력이지만, 향후 표준에 auto의 재정에 더 많은 합리적이 될 것

for(std::vector<foo>::const_iterator i = theContainer.begin(); i != theContainer.end; ++i) 

입니다. 이것은 모든 표준 컨테이너에서 작동합니다. foo*i으로 지칭하고 주소를 원하면 &*i을 사용하십시오.

루프에서 매번 .size()이 실행됩니다. 그러나 모든 표준 컨테이너에 대해 일정한 시간 (표준, 23.1/5)이므로 아무리 많이 느려지지는 않습니다. 추가 : 표준은 "일정한"복잡성을 가져야한다고 말합니다. 따라서 특히 나쁜 구현은 일정하지 않게 만들 수 있습니다. 이러한 잘못된 구현을 사용하는 경우 걱정할 다른 성능 문제가 있습니다.

1

아니요, 올바른 방법이 아닙니다. ::std::vector 또는 ::std::string의 경우 제대로 작동하지만 문제는 다른 것을 사용하면 잘 작동하지 않는다는 것입니다. 또한, 관용적이지 않습니다.

그리고 다른 질문에 대답하십시오 ... size 함수가 아마도 인라인입니다. 즉, 또는 ::std::vector의 내부 값을 가져올 가능성이 높습니다. 컴파일러는이를 최적화하고 대부분의 경우 한 번만 가져옵니다.

for (::std::vector<Foo>::iterator i = theContainer.begin(); 
    i != theContainer.end(); 
    ++i) 
{ 
    Foo &cur_element = *i; 
    // Do stuff 
} 

++i은 매우 중요하다

는하지만, 여기에 관용적 인 방법입니다. 다시 말하지만, 반복자가 기본적으로 포인터 인 ::std:vector 또는 ::std::string의 경우 중요하지 않습니다. 그러나보다 복잡한 데이터 구조의 경우 그렇습니다. i++은 이전 값을 고수해야하기 때문에 복사본을 만들어 임시로 만들어야합니다. ++i에는 아무런 문제가 없습니다. 설득력있는 이유가없는 한 항상 ++i을 사용하는 습관을 가지십시오.

마지막으로 theContainer.end()은 일반적으로 존재하지 않도록 최적화됩니다. 그러나이 수행하여 조금 더 나은 물건을 강제 할 수

for (Foo &i: theContainer) 
{ 
    // Do stuff with i 
} 

이 것 : 물론

const ::std::vector<Foo>::iterator theEnd = theContainer.end(); 

for (::std::vector<Foo>::iterator i = theContainer.begin(); i != theEnd; ++i) 
{ 
    Foo &cur_element = *i; 
    // Do stuff 
} 

을, C++ 0X 상당히 for 루프에 대한 새로운 구문이 모든 것을 단순화 표준 픽스 크기의 배열뿐만 아니라 beginend을 정의하여 반복자와 같은 것을 반환하는 모든 유형에서 작동합니다.

1

네이티브 for-loop (특히 인덱스 기반) - C-way이고 C++이 아닙니다.

루프에 BOOST_FOREACH를 사용하십시오.

정수의 컨테이너, 비교 :

typedef theContainer::const_iterator It; 
for(It it = theContainer.begin(); it != theContainer.end(); ++it) { 
    std::cout << *it << std::endl; 
} 

BOOST_FOREACH (int i, theContainer) { 
    std::cout << i << std::endl; 
} 

그러나 이것은 완벽한 방법을하지. 루프없이 작업을 수행 할 수 있다면 루프없이 수행해야합니다. 예를 들어 알고리즘 및 부스트가 있습니다.피닉스 :

boost::range::for_each(theContainer, std::cout << arg1 << std::endl); 

나는이 솔루션은 코드에서 추가 종속성을 가지고 이해하지만 부스트는 현대의 C++에 대한 '-이 있어야합니다'.

6

C++ 11에는 컴파일러가 새로운 표준을 지원할 경우 사용할 수있는 루프 구문을 인식하는 새로운 컨테이너가 있습니다.

#include <iostream> 
#include <vector> 
#include <string> 

using namespace std; 

int main() 
{ 
    vector<string> vs; 
    vs.push_back("One"); 
    vs.push_back("Two"); 
    vs.push_back("Three"); 

    for (const auto &s : vs) 
    { 
     cout << s << endl; 
    } 

    return 0; 
} 
+3

C++ 11에서는' vs = { "One", "Two", "Three"};'를 쓸 수 있습니다. –

관련 문제