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

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

#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를 사용하고 있습니다.



답변

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

주석 처리 할 때 런타임 오류가 발생하는 이유 #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이 부분적으로 만 포함 된다는 것을 의미합니까 ?

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


답변

문제는 코드가 무한 재귀를 수행한다는 것입니다. 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 <<‘: 모든 제어 경로에서 재귀 적이며 함수로 인해 런타임 스택 오버플로가 발생합니다.


답변