Oto mój przykładowy kod:
#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;
}
Jeśli wykomentuję, #include <string>
nie otrzymam żadnego błędu kompilatora, myślę, że jest to rodzaj dołączenia #include <iostream>
. Jeśli kliknę prawym przyciskiem myszy -> Idź do definicji w programie Microsoft VS, oba wskazują ten sam wiersz w xstring
pliku:
typedef basic_string<char, char_traits<char>, allocator<char> >
string;
Ale kiedy uruchamiam program, pojawia się błąd wyjątku:
0x77846B6E (ntdll.dll) w OperatorString.exe: 0xC00000FD: przepełnienie stosu (parametr: 0x00000001, 0x01202FC4)
Masz jakieś pojęcie, dlaczego podczas komentowania pojawia się błąd wykonania #include <string>
? Używam VS 2013 Express.
c++
string
stack-overflow
explicit
samolotowy
źródło
źródło
#include<iostream>
i<string>
oba mogą obejmować<common/stringimpl.h>
....\main.cpp(23) : warning C4717: 'operator<<': recursive on all control paths, function will cause runtime stack overflow
cl /EHsc main.cpp /Fetest.exe
Odpowiedzi:
Rzeczywiście, bardzo ciekawe zachowanie.
W przypadku kompilatora MS VC ++ błąd występuje, ponieważ jeśli tego nie
#include <string>
zrobisz, nieoperator<<
zdefiniujesz dlastd::string
.Kiedy kompilator próbuje skompilować
ausgabe << f.getName();
, szukaoperator<<
zdefiniowanego plikustd::string
. Ponieważ nie zostało zdefiniowane, kompilator szuka alternatyw. Istniejeoperator<<
zdefiniowane forMyClass
i kompilator próbuje go użyć, a aby go użyć, musi dokonać konwersjistd::string
naMyClass
i tak właśnie się dzieje, ponieważMyClass
ma niejawny konstruktor! Tak więc kompilator kończy tworzenie nowej instancji TwojegoMyClass
i próbuje ponownie przesłać strumieniowo do strumienia wyjściowego. Powoduje to nieskończoną rekursję:Aby uniknąć błędu, musisz
#include <string>
upewnić się, żeoperator<<
zdefiniowano dlastd::string
. Powinieneś takżeMyClass
wyraźnie określić konstruktor, aby uniknąć tego rodzaju nieoczekiwanej konwersji. Zasada mądrości: uczyń konstruktory jawnymi, jeśli przyjmują tylko jeden argument, aby uniknąć niejawnej konwersji:To wygląda
operator<<
nastd::string
dostanie zdefiniowane tylko wtedy, gdy<string>
jest włączone (z kompilatora MS) iz tego powodu wszystko kompilacji, jednak można dostać nieco nieoczekiwane zachowanie jakoperator<<
jest uzyskiwanie nazwie rekurencyjnie dlaMyClass
zamiast dzwonićoperator<<
dostd::string
.Nie, ciąg jest w pełni uwzględniony, w przeciwnym razie nie byłbyś w stanie go użyć.
źródło
std::string
bez#include<string>
wszelkiego rodzaju rzeczy może się zdarzyć, nie ograniczając się do błędu czasu kompilacji. Wywołanie niewłaściwej funkcji lub niewłaściwego operatora to najwyraźniej inna opcja.Problem polega na tym, że Twój kod wykonuje nieskończoną rekurencję. Operator przesyłania strumieniowego dla
std::string
(std::ostream& operator<<(std::ostream&, const std::string&)
) jest zadeklarowany w<string>
pliku nagłówkowym, chociażstd::string
sam jest zadeklarowany w innym pliku nagłówkowym (dołączonym przez oba<iostream>
i<string>
).Jeśli nie
<string>
dołączasz kompilatora, kompilator próbuje znaleźć sposób na kompilacjęausgabe << f.getName();
.Zdarza się, że zdefiniowałeś zarówno operator przesyłania strumieniowego dla, jak
MyClass
i konstruktor, który dopuszcza astd::string
, więc kompilator używa go (poprzez niejawną konstrukcję ), tworząc wywołanie rekurencyjne.Jeśli zadeklarujesz
explicit
swój konstruktor (explicit MyClass(const std::string& s)
), twój kod nie będzie już kompilowany, ponieważ nie ma możliwości wywołania operatora przesyłania strumieniowego za pomocąstd::string
, a będziesz zmuszony dołączyć<string>
nagłówek.EDYTOWAĆ
Moje środowisko testowe to VS 2010 i począwszy od poziomu ostrzeżenia 1 (
/W1
) ostrzega Cię o problemie:źródło