Czy C ++ zapewnia gwarancję na cały okres istnienia zmiennej tymczasowej, która jest tworzona w wywołaniu funkcji, ale nie jest używana jako parametr? Oto przykładowa klasa:
class StringBuffer
{
public:
StringBuffer(std::string & str) : m_str(str)
{
m_buffer.push_back(0);
}
~StringBuffer()
{
m_str = &m_buffer[0];
}
char * Size(int maxlength)
{
m_buffer.resize(maxlength + 1, 0);
return &m_buffer[0];
}
private:
std::string & m_str;
std::vector<char> m_buffer;
};
A oto jak byś tego użył:
// this is from a crusty old API that can't be changed
void GetString(char * str, int maxlength);
std::string mystring;
GetString(StringBuffer(mystring).Size(MAXLEN), MAXLEN);
Kiedy zostanie wywołany destruktor tymczasowego obiektu StringBuffer? Czy to:
- Przed wywołaniem GetString?
- Po zwrocie GetString?
- Zależny od kompilatora?
Wiem, że C ++ gwarantuje, że lokalna zmienna tymczasowa będzie ważna, dopóki istnieje do niej odwołanie - czy dotyczy to obiektów nadrzędnych, gdy istnieje odwołanie do zmiennej składowej?
Dzięki.
m_str.reserve(maxlength)
wchar * Size(int maxlength)
przeciwnym razie destruktor może rzucić.Odpowiedzi:
Destruktor dla tego rodzaju tymczasowych jest wywoływany na końcu pełnego wyrażenia. To najbardziej zewnętrzne wyrażenie, które nie jest częścią żadnego innego wyrażenia. Dzieje się tak w twoim przypadku po zwróceniu funkcji i ocenie wartości. Więc wszystko będzie dobrze działać.
W rzeczywistości to właśnie sprawia, że szablony wyrażeń działają: mogą przechowywać odniesienia do tego rodzaju tymczasowych w wyrażeniu takim jak
Ponieważ każdy tymczasowy będzie trwał do wyrażenia
Jest oceniany w całości. Jest to dość zwięźle opisane w
12.2 Temporary objects
normie.źródło
printf("%s", strdup(std::string("$$$").c_str()) );
Mam na myśli, że jeślistrdup(std::string("$$$").c_str())
jest traktowane jako pełne wyrażenie, to wskaźnik, którystrdup
widzi, jest prawidłowy . Jeślistd::string("$$$").c_str()
jest pełnym wyrażeniem, to wskaźnik, którystrdup
widzi, jest nieprawidłowy ! Czy mógłbyś wyjaśnić trochę więcej na podstawie tego przykładu?printf
jest pełna ekspresja. W związku z tymstrdup
jest to niepotrzebny wyciek pamięci - możesz po prostu pozwolić muc_str()
bezpośrednio wydrukować .odpowiedź litb jest trafna. Czas życia obiektu tymczasowego (znanego również jako wartość r) jest powiązany z wyrażeniem, a destruktor obiektu tymczasowego jest wywoływany na końcu pełnego wyrażenia, a gdy wywoływany jest destruktor na StringBuffer, destruktor na m_buffer również będzie wywoływana, ale nie destruktor na m_str, ponieważ jest referencją.
Zauważ, że C ++ 0x zmienia rzeczy tylko trochę, ponieważ dodaje odwołania do rvalue i semantykę przenoszenia. Zasadniczo, używając parametru referencyjnego rvalue (oznaczonego znakiem &&) mogę „przenieść” wartość r do funkcji (zamiast ją kopiować), a czas życia wartości r można powiązać z obiektem, do którego się przenosi, a nie z wyrażeniem. Jest naprawdę dobry post na blogu zespołu MSVC, który szczegółowo omawia ten temat i zachęcam do przeczytania go.
Pedagogicznym przykładem przenoszenia rwartości są tymczasowe ciągi znaków i pokażę przypisanie w konstruktorze. Jeśli mam klasę MyType, która zawiera zmienną składową typu string, można ją zainicjować za pomocą rvalue w konstruktorze w następujący sposób:
To fajne, ponieważ kiedy deklaruję wystąpienie tej klasy z tymczasowym obiektem:
dzieje się tak, że unikamy kopiowania i niszczenia tymczasowego obiektu, a słowo „hello” jest umieszczane bezpośrednio w zmiennej składowej instancji klasy będącej właścicielem. Jeśli obiekt ma większą wagę niż „string”, dodatkowe wywołanie copy i destructor może być znaczące.
źródło
Po wywołaniu GetString zwraca.
źródło
StringBuffer znajduje się w zakresie GetString. Powinien zostać zniszczony na końcu zakresu GetString (tj. Po powrocie). Nie wierzę też, że C ++ gwarantuje, że zmienna będzie istnieć tak długo, jak długo będzie istniała referencja.
Należy skompilować:
źródło
Napisałem prawie dokładnie tę samą klasę:
Przed standardem każdy kompilator robił to inaczej. Wydaje mi się, że stary podręcznik z adnotacjami dotyczący języka C ++ określał, że elementy tymczasowe powinny zostać wyczyszczone na końcu zakresu, więc niektóre kompilatory to zrobiły. Dopiero w 2003 roku odkryłem, że zachowanie nadal istnieje domyślnie w kompilatorze Sun's Forte C ++, więc StringBuffer nie działa. Ale byłbym zdziwiony, gdyby jakikolwiek aktualny kompilator nadal był tak zepsuty.
źródło