2013-01-23 22 views
5

Jak rozumiem tymczasowe, poniższy kod powinien działać, ale tak nie jest.C++ temporary - "czysta wirtualna metoda o nazwie"

struct base 
{ 
    virtual~base() {} 
    virtual void virt()const=0; 
}; 
struct derived:public base 
{ 
    virtual void virt()const {} 
}; 

const base& foo() {return derived();} 

int main() 
{ 
    foo().virt(); 
    return 0; 
} 

Wywołanie funkcji virt() daje "czystą funkcję wirtualną o nazwie" błąd. Dlaczego tak jest i co powinienem zrobić?

Odpowiedz

5

Wygląda na to, że spodziewasz się, że odniesienie const wydłuży okres użytkowania tymczasowego. Są pewne sytuacje, w których tak się nie dzieje. Jedna z tych sytuacji polega na zwrocie tymczasowego:

Drugi kontekst [w którym czasowniki są niszczone w innym punkcie niż koniec pełnego wyrażenia] to sytuacja, w której odniesienie jest powiązane z tymczasowym. Tymczasowy, do którego odwołanie jest związana lub tymczasowy że pełna przedmiotem podobiektu do których odniesienia związanego utrzymuje się przez cały okres odniesienia z wyjątkiem:

[...]

  • The czas życia tymczasowego powiązania z zwracaną wartością w instrukcji return funkcji (6.6.3) nie jest przedłużany; tymczasowy jest niszczony na końcu pełnego wyrażenia w oświadczeniu zwrotnym.

Ponieważ wywołanie funkcji składowej obiektu zwróconego przez foo() będzie wymagało lwartością-to-rvalue nawrócenia i obiekt jest nieprawidłowy (nie pochodzi od typu base), masz niezdefiniowanej zachowanie:

Jeśli obiekt, którego dotyczy glvalue, nie jest obiektem typu T i nie jest obiektem typu pochodnego od T, lub jeśli obiekt nie jest zainicjowany, program, który wymaga tej konwersji, ma niezdefiniowane zachowanie.

8

Powracasz do odwołania do tymczasowego, który został zniszczony , gdy funkcja kończy się na końcu instrukcji return, a otrzymasz niezdefiniowane zachowanie.

Nie możesz zwrócić żadnego odniesienia do tymczasowego, a jeśli zwrócisz wartość base według wartości, otrzymasz cięcie, więc jeśli naprawdę chcesz, aby to zadziałało, powinieneś zwrócić wartość std::unique_ptr<base>.

+0

dokładnie, ponieważ wirtualna tabela również zostanie oczyszczona ... stąd komunikat. –

+2

@DougT .: Nie ma "ponieważ" w "nieokreślonym zachowaniu". (Poza tym wirtualne tabele * nigdy nie zostaną "wyczyszczone".) –

+0

Ale czy tymczasowy obiekt nie powinien zostać zniszczony na końcu pełnego wyrażenia, to znaczy po powrocie virt()? – Dave

3

Nie sądzę, że możesz zwrócić referencje do takich obiektów. Instancja deriv() wykracza poza zakres, gdy tylko foo() zwróci. Mają

base *foo() { return new derived(); } 

i

foo()->virt(); 

powinno działać.

+3

To powinno zadziałać, ale nie musimy teraz przekazywać surowych wskazówek ... –

+2

Oczywiście, jeśli chcesz użyć shared_ptr <> lub podobnych, to powinieneś zrobić to w prawdziwym kodzie. Jednakże moim punktem jest zasadniczo to, że musimy przydzielić obiekt na stercie, aby to działało, dlaczego ukryć ten fakt pod płaszczykiem lęku-wskaźnika? – Johannes

+1

@Johannes: Ponieważ to tylko pytanie o wycieki pamięci. Inteligentne wskaźniki znajdują się w standardowej bibliotece - użyj ich! : -] – ildjarn

0

Podczas połączenia nie ma żadnego obiektu, obiekt lokalny został usunięty. Nie będzie vtable do obejrzenia. Wypróbuj:

base& foo() { return *new derived(); } 
+0

W jaki sposób dzwoniący wie, że musi zwolnić pamięć? –

0

Twój tymczasowy obiekt "wyprowadzony()" jest przydzielany na stosie. Może to mieć coś wspólnego z rzutem do góry.

baza const & foo() {pochodna * d = nowa pochodna(); return * d; }

Działa dobrze dla mnie.

+1

Wyciekająca pamięć ma _nie_ działa poprawnie. ; -] – ildjarn

+0

Pewnie- dzięki za złapanie. Mógłbym użyć boost :: shared_ptr <> – Arcturus

0

Zmień kawałek problemu to:

class A { 
public: 
    const base &foo() { return d; } 
private: 
    derived d; 
}; 

ten sposób żywotność obiektu pochodzącego jest tak długo, jak życiu każdego, a nigdy nie będzie żadnych problemów.

+0

Co jeśli skonstruowałem A na stosie funkcji 'bar' i zwróciłem odwołanie do' d' z 'bar'? –

Powiązane problemy