¿C ++ proporciona una garantía durante la vida útil de una variable temporal que se crea dentro de una llamada de función pero no se usa como parámetro? Aquí hay una clase de ejemplo:
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;
};
Y así es como lo usaría:
// 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);
¿Cuándo se llamará al destructor del objeto StringBuffer temporal? Lo es:
- ¿Antes de la llamada a GetString?
- ¿Después de que GetString regrese?
- ¿Depende del compilador?
Sé que C ++ garantiza que una variable temporal local será válida siempre que haya una referencia a ella; ¿esto se aplica a los objetos principales cuando hay una referencia a una variable miembro?
Gracias.
m_str.reserve(maxlength)
enchar * Size(int maxlength)
caso contrario, el destructor podría lanzar.Respuestas:
El destructor para ese tipo de temporales se llama al final de la expresión completa. Esa es la expresión más externa que no forma parte de ninguna otra expresión. Eso es en su caso después de que la función regrese y se evalúe el valor. Entonces, todo funcionará bien.
De hecho, es lo que hace que las plantillas de expresión funcionen: pueden mantener referencias a ese tipo de temporales en una expresión como
Porque cada temporal durará hasta la expresión
Se evalúa por completo. Se describe de manera bastante concisa
12.2 Temporary objects
en el Estándar.fuente
printf("%s", strdup(std::string("$$$").c_str()) );
? Quiero decir, sistrdup(std::string("$$$").c_str())
se toma como la expresión completa, entonces el puntero questrdup
ve es válido . Sistd::string("$$$").c_str()
es una expresión completa, entonces el puntero questrdup
ve no es válido . ¿Podría explicar un poco más en base a este ejemplo?printf
es la expresión completa. Por lo tanto,strdup
es una pérdida de memoria innecesaria; puede dejar que imprimac_str()
directamente.La respuesta de litb es precisa. El tiempo de vida del objeto temporal (también conocido como rvalue) está vinculado a la expresión y el destructor del objeto temporal se llama al final de la expresión completa y cuando se llama al destructor en StringBuffer, el destructor en m_buffer también será llamado, pero no el destructor en m_str ya que es una referencia.
Tenga en cuenta que C ++ 0x cambia las cosas solo un poco porque agrega referencias rvalue y mueve semántica. Esencialmente, al usar un parámetro de referencia rvalue (anotado con &&) puedo 'mover' el rvalue a la función (en lugar de copiarlo) y la vida útil del rvalue puede vincularse al objeto al que se mueve, no a la expresión. Hay una publicación de blog realmente buena del equipo de MSVC que explica esto con gran detalle y animo a la gente a leerlo.
El ejemplo pedagógico para mover rvalue son cadenas temporales y mostraré la asignación en un constructor. Si tengo una clase MyType que contiene una variable miembro de cadena, se puede inicializar con un rvalue en el constructor así:
Esto es bueno porque cuando declaro una instancia de esta clase con un objeto temporal:
lo que sucede es que evitamos copiar y destruir el objeto temporal y "hola" se coloca directamente dentro de la variable miembro de la instancia de la clase propietaria. Si el objeto pesa más que una 'cadena', la copia adicional y la llamada al destructor pueden ser importantes.
fuente
Después de que regrese la llamada a GetString.
fuente
StringBuffer está en el ámbito de GetString. Debería destruirse al final del alcance de GetString (es decir, cuando regrese). Además, no creo que C ++ garantice que existirá una variable siempre que haya una referencia.
Se debe compilar lo siguiente:
fuente
Escribí casi exactamente la misma clase:
Antes del estándar, cada compilador lo hacía de manera diferente. Creo que el antiguo Manual de referencia anotado para C ++ especificaba que los temporales deberían limpiarse al final del alcance, por lo que algunos compiladores hicieron eso. Todavía en 2003, descubrí que el comportamiento todavía existía de forma predeterminada en el compilador Forte C ++ de Sun, por lo que StringBuffer no funcionó. Pero me sorprendería si algún compilador actual estuviera tan roto.
fuente