#İnclude <string> burada bir yığın taşması hatasını neden önlüyor?


121

Bu benim örnek kodum:

#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;
}

Eğer yorum #include <string>yaparsam herhangi bir derleyici hatası almazsam, sanırım bir nevi içerdiği için #include <iostream>. Ben ise "sağ tıklama -> Definition git" Microsoft VS onlar aynı çizgiye hem noktası xstringdosyası:

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

Ancak programımı çalıştırdığımda bir istisna hatası alıyorum:

OperatorString.exe'de 0x77846B6E (ntdll.dll): 0xC00000FD: Yığın taşması (Parametre: 0x00000001, 0x01202FC4)

Yorum yaparken neden bir çalışma zamanı hatası aldığım hakkında bir fikriniz var #include <string>mı? VS 2013 Express kullanıyorum.


4
Tanrı'nın lütfuyla. gcc üzerinde mükemmel çalışıyor, ideone.com/YCf4OI
adresine

Visual Studio'yu görsel c ++ ile denediniz mi ve <string> dahil yorum yaptınız mı?
Havadaki

1
@cbuchart: Soru zaten cevaplanmış olmasına rağmen, bence bu yeterince karmaşık bir konu ve farklı kelimelerde ikinci bir cevaba sahip olmak değerli. Harika cevabınızı geri almak için oy verdim.
Orbit'te Hafiflik Yarışları

5
@Ruslan: Etkili, öyle. Demek ki, #include<iostream>ve <string>her ikisi de içerebilir <common/stringimpl.h>.
MSalters

3
Visual Studio 2015'te, ...\main.cpp(23) : warning C4717: 'operator<<': recursive on all control paths, function will cause runtime stack overflowbu satırı çalıştırdığınızda uyarı alırsınızcl /EHsc main.cpp /Fetest.exe
CroCo

Yanıtlar:


161

Gerçekten çok ilginç bir davranış.

Yorum yaparken neden çalışma zamanı hatası aldığım konusunda herhangi bir fikrim var #include <string>

MS VC ++ derleyicisinde hata olur çünkü yapmazsanız #include <string>, için operator<<tanımlamamışsınızdır std::string.

Derleyici çalıştığında derlemek ausgabe << f.getName();bir arar operator<<için tanımlanan std::string. Tanımlanmadığı için derleyici alternatifler arar. İçin operator<<tanımlanmışMyClass ve derleyici onu kullanmaya çalışır ve onu kullanmak için dönüştürmek std::stringzorundadır MyClassve bu tam olarak olan şeydir çünkü MyClassaçık olmayan bir kurucuya sahiptir! Bu nedenle, derleyici yeni bir örneğinizi oluşturur MyClassve bunu çıktı akışınıza yeniden aktarmaya çalışır. Bu, sonsuz bir özyineleme ile sonuçlanır:

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

Hatayı önlemek için yapmanız gerekenler #include <string> , için operator<<tanımlanmış olduğundan eminstd::string . Ayrıca, MyClassbu tür beklenmedik dönüşümlerden kaçınmak için kurucunuzu açıkça belirtmelisiniz. Bilgelik kuralı: örtük dönüşümü önlemek için yalnızca bir argüman alırlarsa yapıcıları açık hale getirin:

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

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

Görünüşe operator<<için std::stringsadece tanımlanmış aldığında <string>(MS derleyici ile) ve bu nedenle her şeyin derlerken için dahildir ancak olarak biraz beklenmeyen davranışlar olsun,operator<< için yinelemeli olarak adlandırılan oluyor MyClassyerine çağıran operator<<için std::string.

Bu, #include <iostream>dize aracılığıyla yalnızca kısmen dahil edildiği anlamına mı geliyor ?

Hayır, dizge tamamen dahil edilmiştir, aksi takdirde onu kullanamazsınız.


19
@airborne - "Visual C ++ 'ya özgü bir sorun" değildir, ancak uygun başlığı eklemediğinizde ne olabilir? Her türlü şey std::stringolmadan kullanıldığında #include<string>, derleme zamanı hatasıyla sınırlı değildir. Yanlış işlevi veya operatörü çağırmak görünüşe göre başka bir seçenektir.
Bo Persson

15
Bu "yanlış işlevi veya operatörü çağırmak" değil; derleyici tam olarak ne yapmasını söylediğinizi yapıyor. Sadece bunu yapmasını söylediğini bilmiyordun;)
Orbit'te Hafiflik Yarışları

18
Bir türün karşılık gelen başlık dosyasını eklemeden kullanılması bir hatadır. Dönemi. Uygulama, hatayı tespit etmeyi kolaylaştırmış olabilir mi? Elbette. Ama bu uygulamayla ilgili bir "sorun" değil , yazdığınız kodla ilgili bir sorun.
Cody Grey

4
Standart kitaplıklar kendi içlerinde std'de başka bir yerde tanımlanan simgeleri dahil etmekte serbesttir ve bir simge tanımlıyorlarsa tüm başlığı dahil etmeleri gerekmez.
Yakk - Adam Nevraumont

5
Bir grup C ++ programcısının derleyicinin ve / veya standart kütüphanenin onlara yardım etmek için daha fazla çalışma yapması gerektiğini tartıştığını görmek biraz komik. Birçok kez işaret edildiği gibi, standarda göre burada uygulama kendi hakları dahilindedir. Bunu programcı için daha açık hale getirmek için "hile" kullanılabilir mi? Elbette, ancak Java'da da kod yazabilir ve bu sorunu tamamen önleyebiliriz. MSVC neden dahili yardımcılarını görünür kılmalıdır? Bir başlık aslında ihtiyaç duymadığı bir grup bağımlılığı neden sürüklesin? Bu, dilin tüm ruhunu ihlal ediyor!
Cody Grey

35

Sorun, kodunuzun sonsuz bir özyineleme yapıyor olmasıdır. std::string( std::ostream& operator<<(std::ostream&, const std::string&)) İçin akış operatörü <string>başlık dosyasında bildirilir , ancak std::stringkendisi başka bir başlık dosyasında bildirilir (hem <iostream>ve hem de tarafından dahil edilir <string>).

Dahil etmediğinizde <string>, derleyici derlemenin bir yolunu bulmaya çalışır ausgabe << f.getName();.

Hem için bir akış operatörü hem de a MyClasskabul eden bir kurucu tanımlamışsınızdır std::string, bu nedenle derleyici bunu kullanır ( örtük oluşturma yoluyla ) ve özyinelemeli bir çağrı oluşturur.

Yapıcınızı explicit( explicit MyClass(const std::string& s)) bildirirseniz, kodunuz artık derlenmeyecektir, çünkü akış operatörünü ile çağırmanın bir yolu yoktur std::stringve <string>başlığı dahil etmek zorunda kalacaksınız .

DÜZENLE

Test ortamım VS 2010 ve 1. uyarı düzeyinden ( /W1) başlayarak sizi sorun hakkında uyarıyor:

uyarı C4717: 'operatör <<': tüm kontrol yollarında özyinelemeli, işlev çalışma zamanı yığın taşmasına neden olur

Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.