#include <string>이 ​​여기서 스택 오버플로 오류를 방지하는 이유는 무엇입니까?


121

이것은 내 샘플 코드입니다.

#include <iostream>
#include <string>
using namespace std;

class MyClass
{
    string figName;
public:
    MyClass(const string& s)
    {
        figName = s;
    }

    const string& getName() const
    {
        return figName;
    }
};

ostream& operator<<(ostream& ausgabe, const MyClass& f)
{
    ausgabe << f.getName();
    return ausgabe;
}

int main()
{
    MyClass f1("Hello");
    cout << f1;
    return 0;
}

내가 주석 처리 #include <string>하면 컴파일러 오류가 발생하지 않는 것 같습니다 #include <iostream>. 내가 만약 "마우스 오른쪽 클릭 -> 정의로 이동" 마이크로 소프트 VS에서 그들이에서 같은 줄에 두 점 xstring파일 :

typedef basic_string<char, char_traits<char>, allocator<char> >
    string;

하지만 프로그램을 실행할 때 예외 오류가 발생합니다.

OperatorString.exe의 0x77846B6E (ntdll.dll) : 0xC00000FD : 스택 오버플로 (매개 변수 : 0x00000001, 0x01202FC4)

주석 처리 할 때 런타임 오류가 발생하는 이유는 #include <string>무엇입니까? VS 2013 Express를 사용하고 있습니다.


4
신의 은혜로. gcc에서 완벽하게 작동합니다. ideone.com/YCf4OI
— v78

Visual C ++로 Visual Studio를 사용 해보고 include <string>을 주석 처리 했습니까?
— 공수

1
@cbuchart : 질문에 대한 답변은 이미 받았지만 다른 단어로 된 두 번째 답변이 가치가있을만큼 복잡한 주제라고 생각합니다. 귀하의 훌륭한 답변을 삭제 취소하기로 투표했습니다.
— 궤도의 가벼운 경주

5
@Ruslan : 실제로 그렇습니다. 그 말을하는 것입니다, #include<iostream>그리고 <string>둘 다 포함 할 수 있습니다 <common/stringimpl.h>.
— MSalters

3
비주얼 스튜디오 2015, 당신은 경고를 얻을 ...\main.cpp(23) : warning C4717: 'operator<<': recursive on all control paths, function will cause runtime stack overflow이 줄을 실행하는cl /EHsc main.cpp /Fetest.exe
— 악어

답변:


161

사실, 매우 흥미로운 행동입니다.

주석 처리 할 때 런타임 오류가 발생하는 이유 #include <string>

그렇게하지 않으면 때문에 MS VC ++ 컴파일러로 오류가 발생 #include <string>하면하지 않습니다 operator<<에 대해 정의 std::string.

컴파일러가 시도 할 때 컴파일 ausgabe << f.getName();그것이 찾습니다 operator<<정의 std::string. 정의되지 않았기 때문에 컴파일러는 대안을 찾습니다. 에 대한 operator<<정의가 있습니다.MyClass 있고 컴파일러가 그것을 사용하려고 시도하고 그것을 사용하려면 변환 std::string해야 MyClass하며 이것은 비명 MyClass시적 생성자를 가지고 있기 때문에 정확히 발생합니다 ! 따라서 컴파일러는 결국 새 인스턴스를 만들고 MyClass출력 스트림으로 다시 스트리밍하려고합니다. 결과적으로 끝없는 재귀가 발생합니다.

 start:
     operator<<(MyClass) -> 
         MyClass::MyClass(MyClass::getName()) -> 
             operator<<(MyClass) -> ... goto start;

오류를 방지하려면 #include <string> 대해 operator<<정의되어 있는지 확인해야합니다 std::string. 또한 MyClass이러한 종류의 예기치 않은 변환을 방지하려면 생성자를 명시 적으로 만들어야합니다 . 지혜의 법칙 : 암시 적 변환을 피하기 위해 하나의 인수 만 사용하는 경우 생성자를 명시 적으로 만듭니다.

class MyClass
{
    string figName;
public:
    explicit MyClass(const string& s) // <<-- avoid implicit conversion
    {
        figName = s;
    }

    const string& getName() const
    {
        return figName;
    }
};

operator<<for std::stringgets <string>가 포함 된 경우 (MS 컴파일러 포함) 에만 정의되는 것처럼 보이며 이러한 이유로 모든 것이 컴파일되지만 예상치 못한 동작이 발생합니다.operator<< 재귀 적 호출지고 MyClass대신 호출 operator<<에 대한을 std::string.

그것은 through #include <iostream>string이 부분적으로 만 포함 된다는 것을 의미합니까 ?

아니요, 문자열은 완전히 포함되어 있습니다. 그렇지 않으면 사용할 수 없습니다.


19
@airborne- "Visual C ++ 특정 문제"는 아니지만 적절한 헤더를 포함하지 않으면 어떤 일이 발생할 수 있습니다. 모든 종류의 일 std::string없이 사용 #include<string>하면 컴파일 시간 오류에 국한되지 않고 발생할 수 있습니다. 잘못된 함수 나 연산자를 호출하는 것은 분명히 또 다른 옵션입니다.
— Bo Persson

15
음, 이것은 "잘못된 함수 나 연산자를 호출하는 것"이 ​​아닙니다. 컴파일러는 당신이 지시 한대로 정확히하고 있습니다. 당신은 당신이 이것을하라고 말하고 있다는 것을 몰랐습니다;)
— 궤도의 Lightness Races

18
해당 헤더 파일을 포함하지 않고 유형을 사용하는 것은 버그입니다. 기간. 구현으로 인해 버그를 더 쉽게 발견 할 수 있었습니까? 확실한. 그러나 이는 구현 의 "문제" 가 아니라 작성한 코드의 문제입니다.
— Cody Gray

4
표준 라이브러리는 자체적으로 std의 다른 곳에 정의 된 토큰을 자유롭게 포함 할 수 있으며 하나의 토큰을 정의하는 경우 전체 헤더를 포함 할 필요가 없습니다.
— Yakk-Adam Nevraumont

5
컴파일러 및 / 또는 표준 라이브러리가 그들을 돕기 위해 더 많은 작업을해야한다고 주장하는 많은 C ++ 프로그래머를 보는 것은 다소 유머러스합니다. 구현은 여러 번 지적 된 것처럼 표준에 따라 여기에서 권리 내에 있습니다. "속임수"를 사용하여 프로그래머에게이를 더 명확하게 할 수 있습니까? 물론입니다.하지만 Java로 코드를 작성하여이 문제를 완전히 피할 수도 있습니다. MSVC가 내부 도우미를 표시해야하는 이유는 무엇입니까? 헤더가 실제로 필요하지 않은 종속성을 끌어 야하는 이유는 무엇입니까? 그것은 언어의 전체 정신을 위반합니다!
— Cody Gray

35

문제는 코드가 무한 재귀를 수행한다는 것입니다. std::string( std::ostream& operator<<(std::ostream&, const std::string&))에 대한 스트리밍 연산자 는 <string>헤더 파일에 선언되어 있지만 std::string자체가 다른 헤더 파일 ( <iostream>및 모두에 포함됨)에 선언되어 <string>있습니다.

포함하지 않으면 <string>컴파일러는 ausgabe << f.getName();.

에 대한 스트리밍 연산자 MyClass와를 허용하는 생성자를 모두 정의 std::string했으므로 컴파일러가이를 사용하여 ( 암시 적 생성을 통해 ) 재귀 호출을 생성합니다.

explicit생성자 ( explicit MyClass(const std::string& s))를 선언하면를 사용 하여 스트리밍 연산자를 호출 할 방법이 없으므로 코드가 더 이상 컴파일되지 않으며 헤더 std::string를 포함해야합니다 <string>.

편집하다

내 테스트 환경은 VS 2010이며 경고 수준 1 ( /W1) 부터 문제에 대해 경고합니다.

경고 C4717 : 'operator <<': 모든 제어 경로에서 재귀 적이며 함수로 인해 런타임 스택 오버플로가 발생합니다.

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