#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.