2013-04-24 22 views
60

Zrobiłem test tutaj, ale wynik jest pętlą bez zakończenia, nie wiem dlaczego.Nieskończona pętla w konstruktorze bez lub podczas

Właściwie robię kolejny test, ale kiedy to napisałem, nie rozumiem, jak powstała pętla. Jest to wynik "ABC" wielokrotnie.

#include <map> 
#include <string> 
#include <iostream> 

class test 
{ 
public: 
    std::map <int, int> _b; 
    test(); 
    test (std::map<int, int> & im); 
    ~test(); 
    }; 

test::test() 
{ 
    std::cout<<"abc"; 
    _b.clear(); 
    _b[1]=1; 
    test(_b); 
} 

test::test(std::map <int, int>& im) 
{ 
    std::cout<<im[1]; 
} 

test::~test() {}; 

int main() 
{ 
    test a; 
} 
+1

To rekursja ze względu na 'test (_b);' ale nie jestem pewien, dlaczego. – Roddy

+0

@ Roddy - Właśnie to rozgryzłem; zobacz moją odpowiedź po szczegóły. – templatetypedef

+1

Oczyszczona wersja z nieistotnymi usuniętymi materiałami: http://ideone.com/z0yc7Q – Yakk

Odpowiedz

94

Problem jest to, że kompilator interpretuje

test(_b); 

Nie jako kodu, który tworzy tymczasowy obiekt typu test przechodzącą w parametrze _b, ale jako zmienna deklaracji zmiennej o nazwie _b typu test, stosując domyślny konstruktor. W związku z tym, co wygląda jak fragment kodu, który tworzy tymczasowy obiekt test przy użyciu drugiego konstruktora, rekursywnie tworzy nowy obiekt typu test i wywołuje konstruktora w innym czasie.

Aby rozwiązać ten problem, można nadać zmiennej jednoznacznej nazwy, takie jak

test t(_b); 

ten można interpretować jedynie jako zmienna typu test nazwie t, zainicjowana przy użyciu drugiego konstruktora.

Mam nigdy widziałem to wcześniej, a ja programowałem w C++ od lat. Dzięki za pokazanie mi kolejnej narożnej wersji języka!

Oficjalne wyjaśnienie: zgodnie ze specyfikacją ISO C++ 03, § 6.8:

Jest dwuznaczność w gramatyce udziałem ekspresję wypowiedzi i deklaracje: Wyrażenie-sprawozdanie z funkcją konwersji stylu jawne typu (5.2.3) jako skrajny lewy podwyrażenie może być nie do odróżnienia od deklaracji gdzie pierwszy declarator zaczyna się od (. W tych przypadkach oświadczenie jest oświadczeniem.

(Moje podkreślenie). innymi słowy, za każdym razem, C++ mógłby zinterpretować oświadczenie albo jako wyraz (tymczasowego obiektu obsadzie) lub jako deklaracja (zmiennej), wybierze deklarację. Specyfikacja C++ jawnie daje

T (a);

Jako przykład deklaracji, a nie odlewania a na coś typu T.

To jest C++ 's Most Vexing Parse - to, co wygląda na wyrażenie, jest zamiast tego interpretowane jako deklaracja. Widziałem już wcześniej MVP, ale nigdy nie widziałem go w tym kontekście.

Mam nadzieję, że to pomoże!

+1

Dzięki, jest to dla Google zaskakująco trudne. – Antimony

+0

Dobra odpowiedź! Wszelkie referencje specyfikacji dostępne do udowodnienia/obalenia, że ​​kompilator interpretuje to poprawnie? FWIW, codepad.org wykazały to samo zachowanie. – Tom

+0

Ach, znalazłem wyjaśnienie. Nazywa się "Most Vexing Parse". Przy okazji, w C++ 11 możesz to naprawić, zmieniając go na 'test {_b}'. (Zamiast nawiasów używaj nawiasów klamrowych) http://en.wikipedia.org/wiki/Most_vexing_parse – Antimony

0

problem jest z konstruktora ponownym wywołaniu testu contructor (_B)

Test :: test() {std :: cout < < "abc"; _ b.clear(); _B [1] = 1; Test (_B);}

tutaj jest to, co dzieje się

każdym wywołaniu testu (_B) najpierw połączenia domyślnego konstruktora testowy :: test i po kolei połączeń test (_b) i pętla trwa aż do przepełnienia stosu.

wyjąć test (_B) z domyślnego konstruktora

+0

Dlaczego wywołuje to domyślny konstruktor? Pomyślałem, że to wywoła niejawny konstruktor konwersji 'test :: test (std :: map &)', ponieważ jawnie przechodzi w '_b'. – templatetypedef

+0

Dlaczego wywołuje domyślny konstruktor? C++ nie łączy konstruktorów. – Antimony

+0

W C++ nie powinieneś wywoływać innego konstruktora. Różni się to na przykład od Javy. – OlivierD

0

Nie jestem zaznajomiony ze specyfiką standardu, ale może się zdarzyć, że wywołanie konstruktora wewnątrz konstruktora jest niezdefiniowana. Jako taki może być zależny od kompilatora. W tym konkretnym przypadku powoduje nieskończoną rekurencję domyślnego konstruktora bez wywoływania twojego konstruktora z argumentem map.

C++ FAQ 10.3 ma przykład z konstruktorem, który ma dwa parametry. Jeśli dodasz parametry typu int do drugiego konstruktora, takiego jak test(map, int), będzie to wyglądało trochę normalnie.

Dla dobrego formularza chciałbym po prostu zmienić test::test(std::map <int, int>& im) dla test::testInit(std::map <int, int>& im) i test(_b) na testInit(_b).

+0

. Wywołuje inny konstruktor, ale nie jest to ten sam konstruktor, co poprzednio. Nie rozumiem, dlaczego spowodowałoby to nieskończoną rekurencję, która jest tutaj wystawiona. – templatetypedef

0

Jestem prawie pewien, że tak naprawdę nie jesteś "wywołanie konstruktora", ponieważ nie są one bezpośrednio wywoływalne IIRC. Legalese miał do czynienia z konstruktorami, które nie są nazwane funkcjami. Nie mam kopii podręcznika standardowego lub mogę go zacytować. Wierzę, że to, co robisz z test(_b), tworzy nienazwany tymczasowy, który ponownie wywołuje domyślny konstruktor.

+0

Nie tworzy on nienazwanego tymczasowego i wywołuje domyślny konstruktor; zamiast tego tworzy zmienną o nazwie '_b' i wywołuje domyślny konstruktor. Zobacz moją odpowiedź, aby uzyskać więcej informacji. – templatetypedef

+0

Dzięki. Przypomniałem sobie, co robi, ale nie z dokładnego powodu. Zapomniałem, jak skręcone są zasady deklaracji C/C++. –

Powiązane problemy