2011-01-06 319 views
8

假设你有一个类至极是一个全球性的(例如可以为应用程序的运行时)如何正确地返回的std :: string(或如何正确使用返回值)

class MyClass { 
    protected: 
    std::string m_Value; 
    public: 
    MyClass() : m_Value("hello") {} 
    std::string value() { return m_Value; }  
}; 

MyClass v1; 

使用第一形式给我奇怪的行为,当我做

printf("value: %s\n", v1.value().c_str()); 

它看起来好像字符串在printf可以使用它之前从内存中消失。 有时它会打印值:其他时候它会崩溃或不打印任何东西。

如果我先复制像这样

std::string copiedString = v1.value(); 
    printf("value: %s\n", copiedString.c_str()); 

事情做的工作的字符串。

当然,必须有一种方法避免使用临时字符串来做到这一点。

编辑:所以共识是用const std :: string &返回值。

我知道每个人都说原始代码应该没问题,但我可以告诉你,我已经看到Windows CE上的MSVC 2005有问题,但只在CE盒子上。不是Win32交叉编译。

+5

你可以发布一个可编译的例子吗?没错,我不明白你所遇到的情况会如何发生。 – Falmarri 2011-01-06 22:11:49

+7

临时的`v1.value()`应该一直存在到完整表达式的末尾,即它不应该在`printf`返回之前被销毁。 – 2011-01-06 22:16:49

+0

@Charles Bailey:真的吗?是不是只是评估从c_str()获得常规字符*? – villintehaspam 2011-01-06 22:20:28

回答

1

好吧,代码没有问题(正如我解释的那样)。这不是最佳的,肯定不是The Right Way (R),你应该像villentehaspam建议的那样修改你的代码。现在,您的代码会创建字符串m_value的副本,因为您按值返回,这不如仅返回const引用。

如果您提供了一个完整的代码示例来显示该问题,我可以帮助您更好。

6

你的代码应该可以正常工作。还有一些错误,我们无法从这个测试用例中发现。也许通过valgrind运行你的可执行文件来搜索内存错误。

0

不是那么重要,但该类中的std :: string返回可以使用const else明智的,你只是创建了一个成员值的副本,这是一个浪费。

std::string value() const { return m_value; } 
-1

这里是什么通常发生,当你写:

printf("value: %s\n", v1.value().c_str()); 
  1. 编译器可以临时std::string保存从v1.value()返回的值。
  2. 它调用v1.value()并将其返回值放入临时字符串中(具体如何实现可能会有所不同:通常它会将临时引用传递给方法的隐藏参数,请参阅http://en.wikipedia.org/wiki/Return_value_optimization进行讨论)。
  3. 它在临时std::string上调用.c_str(),它将const char *结果存储在某处(例如寄存器)。
  4. 它现在已经完成临时std::string,因此破坏它(即调用它的析构函数,也许释放一些堆栈空间)。
  5. 它将步骤(3)中获取的const char *指针作为参数传递给printf()

问题是,步骤3中的指针指向由临时std::string分配的内存,当临时的析构函数被调用时它被释放。当它被printf()使用时,该内存可能早已消失。

基本上,任何类似你所显示的用法都是危险的,应该避免。使用以下是正确的:

std::string copiedString = v1.value(); 
printf("value: %s\n", copiedString.c_str()); 

因为copiedString析构函数不会被调用,直到copiedString超出范围,printf()一段时间后,又回来了。实际上,这不会比v1.value().c_str()有效,因为在任何情况下都会创建临时std::string

返回对字符串的引用是一个很好的选择,前提是只要调用者需要它,引用仍然有效。所以,对一个长寿命对象的成员变量的引用是OK的;对事物的引用是暂时的不是。