6

我还在学习c++,所以请多多包涵。我正在围绕 boost 文件系统路径编写一个简单的包装器——我在返回临时字符串时遇到了奇怪的问题。这是我的简单课程(这不准确,但非常接近):

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;

}

使用下面的小测试代码:

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

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

我很确定这必须处理临时人员,但我无法弄清楚为什么会这样 - 不应该一切都正确复制吗?

4

3 回答 3

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

mPath.string()返回 a 的副本std::string。该副本存储在临时文件中,该临时文件将在此表达式结束时被销毁。

.c_str()返回一个指向内存的指针,当字符串被销毁时,该内存将被销毁,即在这个表达式的末尾。

您正在返回一个指向已被破坏的内存的指针。

于 2012-07-06T19:32:20.907 回答
4

看起来像mPath.string()按值返回一个字符串。一旦 FileReference::c_str() 返回,该临时字符串对象就会被破坏,因此它的 c_str() 变得无效。c_str()使用这样的模型,如果不为字符串引入某种类或静态级别的变量,就不可能创建一个函数。

考虑以下替代方案:

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

或者

void str(String &s) const
{             
     s = mPath.string();
} 
于 2012-07-06T19:29:26.137 回答
0

表达式

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

OutputDebugString(file.c_str())

相似之处在于它们都有效地调用c_str()临时std::string对象的方法并尝试使用该调用的结果。第一个直接将其称为file.path().c_str()子表达式。第二个更隐含地做到了:内部FileReference::c_str()方法。

在第一种情况下,临时std::string对象是通过file.path()调用显式创建的,作为整个表达式的直接部分。根据语言规则,该临时对象的生命周期延伸到整个表达式的末尾,这就是为什么临时对象和c_str()调用结果始终保持有效的原因。

在第二种情况下,临时std::string对象是在FileReference::c_str()方法内部创建的。当此方法返回时,该临时对象被销毁,这意味着FileReference::c_str()返回指向“死”数据的指针。这就是有问题的“垃圾”的原因。

于 2012-07-06T20:46:56.240 回答