vector :: at 대 vector :: operator []


95

C ++ Vector at / [] operator speed 또는 :: std :: vector :: at () vs operator [] << 와 같은 유사한 질문에서도 논의되는 경계 검사로 인해 at()속도가 느리다는 것을 알고 []있습니다 . 5 ~ 10 배 더 느리게 / 빠르게! . 나는 그 방법이 무엇에 좋은지 이해하지 못합니다 .at()

이와 같은 간단한 벡터가있는 경우 : 인덱스가있는 상황에서 대신 std::vector<int> v(10);사용하여 해당 요소에 액세스하기로 결정하고 벡터 내 경계가 있는지 확실하지 않으면 try-catch래핑해야합니다. 블록 :at()[]i

try
{
    v.at(i) = 2;
}
catch (std::out_of_range& oor)
{
    ...
}

size()색인을 직접 사용 하고 확인 하여 동일한 동작을 수행 할 수 있지만 이는 나에게 더 쉽고 편리해 보입니다.

if (i < v.size())
    v[i] = 2;

그래서 내 질문은 : vector :: operator [] 보다 vector :: at
사용의 장점은 무엇입니까 ? vector :: size 대신 vector :: at를
사용해야 하는 경우 + vector :: operator [] 합니까?


11
+1 아주 좋은 질문 !! 하지만 나는 at ()이 일반적으로 사용되는 것이라고 생각하지 않습니다.
Rohit Vipin Mathews 2012

10
예제 코드 if (i < v.size()) v[i] = 2;에는의 2어떤 요소에도 할당되지 않는 가능한 코드 경로가 v있습니다. 그것이 올바른 행동이라면 좋습니다. 그러나 종종이 함수가 i >= v.size(). 따라서 예기치 않은 상황을 나타 내기 위해 예외를 사용 하지 않아야 하는 특별한 이유 가 없습니다 . 많은 함수 operator[]는 크기, i범위 내에 있어야하는 문서를 확인하지 않고 사용 하고 결과 UB를 호출자에게 비난합니다.
Steve Jessop 2012

답변:


74

vector::at()던지는 예외 는 바로 주변 코드에 의해 잡히도록 의도 된 것이 아닙니다. 주로 코드에서 버그를 잡는 데 유용합니다. 예를 들어 인덱스가 사용자 입력에서 나오기 때문에 런타임에 경계 검사가 필요한 경우 실제로 if명령문을 사용하는 것이 가장 좋습니다 . 요약하면, vector::at()예외를 던지지 않을 의도로 코드를 디자인하십시오. 그러면 예외가 발생하고 프로그램이 중단되면 버그의 신호입니다. (처럼 assert())


1
+1 잘못된 사용자 입력 (입력 유효성 검사, 잘못된 입력이 예상 될 수 있으므로 예외적 인 것으로 간주되지 않음) 처리를 분리하는 방법에 대한 설명이 마음에 듭니다 ... 그리고 코드의 버그 (범위를 벗어난 반복기의 비정의는 예외입니다) 일)
Bojan Komazec 2012

따라서 색인이 사용자 입력에 의존 할 때 size()+를 사용해야한다고 말하고 , 나중에 쉽게 버그를 수정하기 위해 색인이 범위를 벗어나서는 안되는 상황과 다른 모든 상황에서 사용해야한다고 말합니다. .)[]assert.at()
LihO

8
@LihO : 구현에서 디버깅 구현을 제공하는 vector경우 at()모든 곳이 아니라 "경우에 따라"옵션으로 사용하는 것이 좋습니다 . 이렇게하면 필요할 때를 대비하여 릴리스 모드에서 더 많은 성능을 기대할 수 있습니다.
Steve Jessop 2012

3
네, 대부분의 STL 구현은 요즘 어떤 경계 - 검사 디버그 모드를 지원하더라도 operator[], 예를 들어 gcc.gnu.org/onlinedocs/libstdc++/manual/... 플랫폼이 지원하는 경우 그래서, 당신이 그것으로 가고 떨어져 아마 최고입니다!
pmdj

1
@pmdj 환상적인 포인트, 내가 몰랐던 ...하지만 고아 링크. : P 현재 : gcc.gnu.org/onlinedocs/libstdc++/manual/debug_mode.html
underscore_d

16

그것은 나를 try-catch 블록으로 감싸도록 강요합니다.

아니요 (try / catch 블록은 업스트림 일 수 있음). 프로그램이 정의되지 않은 동작 영역에 들어가는 대신 예외가 발생하기를 원할 때 유용합니다.

벡터에 대한 대부분의 경계를 벗어난 액세스는 프로그래머의 실수라는 데 동의합니다 (이 경우 assert이러한 실수를 더 쉽게 찾을 수 있도록 사용해야 합니다. 대부분의 표준 라이브러리 디버그 버전은 자동으로 수행합니다). 프로그래머 실수를보고하기 위해 업스트림으로 삼킬 수있는 예외를 사용하고 싶지 않습니다 . 버그수정할 수 있기를 원합니다 .

벡터에 대한 경계를 벗어난 액세스가 정상적인 프로그램 흐름의 일부일 가능성이 낮기 때문에 (그렇다면 당신이 옳습니다 : size예외가 발생하도록하는 대신 미리 확인하십시오 ), 귀하의 진단에 동의합니다. at본질적으로 쓸모가 없습니다.


out_of_range예외를 포착하지 않으면 abort()호출됩니다.
LihO

@LihO : 반드시 그런 것은 아닙니다 try..catch.이 메서드를 호출하는 메서드에있을 수 있습니다.
Naveen

12
다른 것이 없다면, atif (i < v.size()) { v[i] = 2; } else { throw what_are_you_doing_you_muppet(); }. 사람들은 종종 "저주, 나는 예외를 처리해야한다"라는 관점에서 예외를 던지는 함수를 생각하지만, 각 함수가 던질 수있는 것을주의 깊게 문서화하는 한, 그것들은 또한 "훌륭해, 나는 그렇지 않다"로 사용될 수있다. 조건을 확인하고 예외를 던져야합니다. "
Steve Jessop 2012

@SteveJessop : 다른 프로그래머가 업스트림에 잡힐 수 있기 때문에 프로그램 버그에 대한 예외를 던지는 것을 좋아하지 않습니다. 여기서 어설 션이 훨씬 더 유용합니다.
Alexandre C.

6
@AlexandreC. 그에 대한 공식적인 반응은에서 out_of_range파생 logic_error되며 다른 프로그래머는 logic_errors를 업스트림 으로 잡아 무시하는 것보다 더 잘 알고 있어야한다는 것입니다. assert동료들이 실수에 대해 알지 못하는 경우에도 무시할 수 있습니다. NDEBUG;-)로 코드를 컴파일해야하기 때문에 더 어렵습니다 . 각 메커니즘에는 장점과 결점이 있습니다.
Steve Jessop 2012

11

vector :: operator []보다 vector :: at를 사용하면 어떤 이점이 있습니까? vector :: size + vector :: operator [] 대신 vector :: at를 언제 사용해야합니까?

여기서 중요한 점은 예외를 사용하면 오류 처리 논리에서 코드의 정상적인 흐름을 분리 할 수 ​​있으며 단일 catch 블록은 함수 호출 내에 흩어져 있어도 무수한 throw 사이트에서 생성 된 문제를 처리 할 수 ​​있다는 것입니다. 따라서 at()한 번만 사용 하는 것이 반드시 더 쉬운 것은 아니지만 유효성을 검사 할 인덱싱이 많을 때 때때로 더 쉬워지고 일반 사례 논리의 난독 화가 줄어 듭니다.

또한 일부 코드 유형에서는 인덱스가 복잡한 방식으로 증가하고 배열을 찾는 데 계속 사용된다는 점도 주목할 만합니다. 이러한 경우 at().

실제 예로서 C ++를 어휘 요소로 토큰 화하는 코드와 토큰 벡터 위로 인덱스를 이동하는 다른 코드가 있습니다. 발생한 사항에 따라 다음과 같이 다음 요소를 증분하고 확인할 수 있습니다.

if (token.at(i) == Token::Keyword_Enum)
{
    ASSERT_EQ(tokens.at(++i), Token::Idn);
    if (tokens.at(++i) == Left_Brace)
        ...
    or whatever

이런 상황 에서 입력의 끝에 부적절하게 도달 했는지 여부를 확인하는 것은 매우 어렵 습니다. 이는 발생하는 정확한 토큰에 매우 의존하기 때문입니다. 각 사용 지점에서의 명시 적 검사는 고통스럽고 사전 / 사후 증분, 사용 지점에서의 오프셋, 일부 이전 테스트의 지속적인 유효성에 대한 잘못된 추론 등으로 인해 프로그래머 오류가 발생할 여지가 훨씬 더 많습니다.


10

at 벡터에 대한 포인터가 있으면 더 명확해질 수 있습니다.

return pVector->at(n);
return (*pVector)[n];
return pVector->operator[](n);

성능을 제외하고 첫 번째는 더 간단하고 명확한 코드입니다.


... 특히 벡터 의 n 번째 요소에 대한 포인터가 필요할 때 .
dolphin

4

첫째, 여부 at()또는 operator[]느리게 지정되지 않습니다. 경계 오류가 없으면 적어도 디버깅 빌드에서 거의 같은 속도 일 것으로 예상합니다. 차이점은 at()경계 오류 (예외)가있을 때 발생할 일을 정확히 지정 한다는 것입니다. 여기서의 경우와 같이 operator[]정의되지 않은 동작입니다. 최소한 사용하는 모든 시스템 (g ++ 및 VC ++)에서 충돌이 발생합니다. 일반 디버깅 플래그가 사용됩니다. (또 다른 차이점은 일단 내 코드를 확신 operator[] 하면 디버깅을 해제 하여 상당한 속도 향상을 얻을 수 있다는 것입니다. 성능에 필요한 경우 — 필요하지 않으면 수행하지 않을 것입니다.)

실제로 at()는 거의 적합하지 않습니다. 만약 인덱스가 유효하지 않다는 것을 알고있는 컨텍스트라면, 아마도 명시적인 테스트를 원할 것입니다 (예를 들어, 기본값이나 무언가를 반환하는 것). 그리고 그것이 유효하지 않다는 것을 알고 있다면 중단하고 싶을 것입니다. 유효하지 않은지 여부를 알지 못하므로 함수의 인터페이스를 더 정확하게 지정하는 것이 좋습니다.) 그러나 몇 가지 예외가 있습니다. 여기서 잘못된 인덱스는 사용자 데이터 구문 분석으로 인해 발생할 수 있으며 오류로 인해 전체 요청이 중단되지만 서버가 중단되지는 않습니다. 이러한 경우 예외가 적절 at()하며 귀하를 위해 그렇게 할 것입니다.


4
operator[]경계 검사를 강요하지 않는 반면에 왜 동일한 속도를 기대할 수 at()있습니까? 캐싱, 추측 및 분기 버퍼 문제를 의미합니까?
Sebastian Mach

@phresnel operator[]은 경계 검사를 수행하는 데 필요하지 않지만 모든 좋은 구현은 수행합니다. 적어도 디버깅 모드에서는. 유일한 차이점은 인덱스가 범위를 벗어난 경우 수행하는 작업 operator[]입니다. 오류 메시지와 함께 중단되고 at()예외가 발생합니다.
James Kanze

2
죄송합니다. "디버깅 모드"속성을 놓쳤습니다. 그러나 디버그 모드에서는 코드 품질을 측정하지 않습니다. 릴리스 모드에서는에서만 확인이 필요합니다 at().
Sebastian Mach

1
@phresnel 제가 전달한 대부분의 코드는 "디버그"모드였습니다. 성능 문제가 실제로 필요할 때만 확인을 해제합니다. (Microsoft 2010 이전 버전 std::string은 검사 옵션이 런타임의 옵션과 일치하지 않으면 항상 작동하지 않았기 때문에 여기에서 약간의 문제 였습니다. -MD, 검사를 끄는 -MDd것이 좋습니다. 에.)
James Kanze

2
나는 "표준에 의해 승인 된 (보장 된) 코드"라고 말하는 캠프에 더 가깝습니다. 물론 디버그 모드에서 자유롭게 제공 할 수 있지만, 크로스 플랫폼 개발을 수행 할 때 (단독적으로는 아니지만 동일한 OS의 경우를 포함하되 컴파일러 버전이 다른 경우) 표준에 의존하는 것이 릴리스 및 디버그 모드에 가장 좋은 방법입니다. :) 대부분 정확하고 강력한 그 일을 얻기 위해 프로그래머를위한 도구로 간주됩니다
세바스찬 마하에게

1

예외 사용의 요점은 오류 처리 코드가 더 멀리 떨어져있을 수 있다는 것입니다.

이 특정 경우에 사용자 입력은 실제로 좋은 예입니다. 인덱스를 사용하여 .NET Framework에 내부적으로 저장하는 일종의 리소스를 참조하는 XML 데이터 구조를 의미 론적으로 분석한다고 가정 해보십시오 std::vector. 이제 XML 트리는 트리이므로 재귀를 사용하여 분석 할 수 있습니다. 재귀에서 XML 파일 작성자의 액세스 위반이있을 수 있습니다. 이 경우 일반적으로 모든 수준의 재귀에서 벗어나 전체 파일 (또는 모든 종류의 "거친"구조)을 거부하려고합니다. 이것이 편리합니다. 파일이 유효한 것처럼 분석 코드를 작성할 수 있습니다. 라이브러리 코드는 오류 감지를 처리하고 대략적인 수준에서 오류를 포착 할 수 있습니다.

또한,와 같은 다른 컨테이너 std::map도 : at std::map::at과 약간 다른 의미를 가지고 있습니다 std::map::operator[]. const 맵에서 operator[]사용할 수 있지만 할 수 없습니다. 이제 당신도 다룰 수있는 일처럼 쓰기 컨테이너 무관 코드를 원하는 경우 const std::vector<T>&또는 const std::map<std::size_t, T>&, ContainerType::at선택의 무기가 될 것입니다.

그러나 이러한 모든 경우는 일반적으로 일종의 검증되지 않은 데이터 입력을 처리 할 때 나타납니다. 당신이 당신의 유효한 범위에 대해 확신하는 경우 일반적으로해야로서, 일반적으로 사용할 수 operator[]있지만, 더 나은 아직,와 반복자 begin()end().


1

기사 에 따르면 성능은 제쳐두고 액세스가 벡터의 크기 내에서 보장되는 경우에만 at또는 을 사용하는 데 아무런 차이가 없습니다 operator[]. 그렇지 않으면 액세스가 벡터의 용량을 기반으로하는 경우 사용하는 것이 더 안전합니다 at.


1
밖에 용이 있습니다. 링크를 클릭하면 어떻게 되나요? (힌트 : 이미 그것을 알고 있지만 StackOverflow의에 우리가 링크 부패를 용납하지 않는다 코멘트, 즉 당신이하고 싶은 말에 대한 간단한 요약을 제공 선호)
세바스찬 마하

팁 고마워. 이제 수정되었습니다.
ahj

0

참고 : 일부 새로운 사람들은 무엇이 잘못되었는지 예의없이이 답변에 반대하는 것으로 보입니다. 아래 답변은 정확하며 여기에서 확인할 수 있습니다 .

실제로 한 가지 차이점이 있습니다. at경계를 확인하는 반면 operator[]그렇지 않은 경우입니다. 이것은 디버그 빌드와 릴리스 빌드에 적용되며 표준에 의해 매우 잘 지정됩니다. 그렇게 간단합니다.

이것은 at더 느린 방법을 만들지 만 사용하지 않는 것은 정말 나쁜 조언 at입니다. 상대 숫자가 아닌 절대 숫자를 봐야합니다. 대부분의 코드가 at. 개인적으로 나는 at정의되지 않은 동작을 생성하고 프로덕션에 몰래 들어가는 불쾌한 버그를 원하지 않기 때문에 사용하려고합니다 .


1
C ++의 예외는 디버깅 도구가 아니라 오류 처리 메커니즘을 의미합니다. Herb Sutter는 던지기 std::out_of_range나 어떤 형태 든 std::logic_error실제로 여기 에서 그 자체로 논리 오류 인 이유를 설명합니다 .
Big Temp

@BigTemp-귀하의 의견이이 질문 및 답변과 어떻게 관련되어 있는지 잘 모르겠습니다. 예, 예외는 논쟁의 여지가 많은 주제이지만 여기서 질문은 atand 의 차이 []이며 내 대답은 단순히 차이점을 나타냅니다. 저는 개인적으로 성능이 문제가되지 않을 때 "안전한"방법을 사용합니다. Knuth가 말했듯이 조기 최적화를하지 마십시오. 또한 철학적 차이에 관계없이 프로덕션보다 일찍 버그를 잡는 것이 좋습니다.
Shital Shah
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.