2012-07-06 6 views
6

Todavía estoy aprendiendo C++, así que por favor tengan paciencia conmigo. Estoy escribiendo un envoltorio simple para mejorar las rutas del sistema de archivos. Tengo problemas extraños con la devolución de cadenas temporales. Aquí está mi clase simple (esto no es exacta, pero bastante cerca):Temporal std :: cadenas devolviendo basura

typedef const char* CString; 
typedef std::string String; 
typedef boost::filesystem::path Path; 

class FileReference { 

    public: 

     FileReference(const char* path) : mPath(path) {}; 

     // returns a path 
     String path() const { 
      return mPath.string(); 
     }; 

     // returns a path a c string 
     CString c_str() const { 
      return mPath.string().c_str(); 
     }; 

    private: 

     Path mPath; 

} 

Con el pequeño código de prueba a continuación:

FileReference file("c:\\test.txt"); 

OutputDebugString(file.path().c_str()); // returns correctly c:\test.txt 
OutputDebugString(file.c_str());  // returns junk (ie îþîþîþîþîþîþîþîþîþîþî.....) 

Estoy bastante seguro de que esto tiene que hacer frente a los temporales, pero No puedo entender por qué sería eso: ¿no debería todo estar copiando correctamente?

+0

¿Cuál es la fuente de 'OutputDebugString()'? –

+2

Cringed at 'typedef''ing your own' CString' ... – Blindy

+1

@OttoAllmendinger - Es una [API de Windows] (http://msdn.microsoft.com/en-us/library/windows/desktop/aa363362 (v = vs.85) .aspx) función. –

Respuesta

4

Parece mPath.string() devuelve una cadena por valor. Ese objeto de cadena temporal se destruye tan pronto como FileReference :: c_str() retorna, por lo que c_str() deja de ser válido. Con un modelo de este tipo, no es posible crear una función c_str() sin introducir algún tipo de variable de nivel estático o de clase para la cadena.

considerar las siguientes alternativas:

//Returns a string by value (not a pointer!) 
//Don't call it c_str() - that'd be misleading 
String str() const 
{    
    return mPath.string(); 
} 

o

void str(String &s) const 
{    
    s = mPath.string(); 
} 
+0

Un uso válido para la primera sugerencia sería 'OutputDebugString (file.str(). c_str()); ' –

8
CString c_str() const { 
     return mPath.string().c_str(); 
    }; 

mPath.string() devuelve una copia de un std::string. Esa copia se almacena en un temporal que se destruirá al final de esta expresión.

.c_str() devuelve un puntero a la memoria que se destruirá cuando se destruya la cadena, es decir, al final de esta expresión.

Está devolviendo un puntero a la memoria que ya está destruida.

+1

si quiere un puntero, puede devolver m_path.c_str() – PermanentGuest

+0

@PermanentGuest - ¿está seguro? No puedo encontrar '.c_str()' en Boost.Filesystem [documentation] (http://www.boost.org/doc/libs/1_41_0/libs/filesystem/doc/reference.html#Class-template- basic_path). –

+0

@ Robᵩ: esos documentos tienen casi tres años. Los [_current_ docs] (http://www.boost.org/doc/libs/1_50_0/libs/filesystem/doc/reference.html#path-native-format-observers) lo mencionan. ; -] (Esto probablemente fue agregado en FileSystem v3.) – ildjarn

0

Expresiones

OutputDebugString(file.path().c_str()) 

y

OutputDebugString(file.c_str()) 

son similares en que ambos llaman efectivamente c_str() método en un objeto temporal y std::string intentan utilizar el resultado de esa llamada. El primero lo llama directamente como subexpresión file.path().c_str(). El segundo lo hace de manera más implícita: dentro del método FileReference::c_str().

En el primer caso, el objeto temporal std::string se crea explícitamente mediante la llamada file.path() como una parte inmediata de toda la expresión. De acuerdo con las reglas del lenguaje, la duración de ese objeto temporal se extiende hasta el final de toda la expresión, por lo que el temporal y el resultado de la llamada c_str() permanecen válidos en todo momento.

En el segundo caso, se crea el objeto temporal std::string dentro del método FileReference::c_str(). Ese objeto temporal se destruye cuando este método retorna, lo que significa que FileReference::c_str() devuelve un puntero a datos "muertos". Esta es la razón de la "basura" en cuestión.