如何在C++中返回一个局部变量的const引用?

4

我有一个函数,但我无法更改其函数参数。我需要返回一个指向此函数中创建的std::string的常量引用。我尝试使用boost shared_ptr,但这不起作用。为什么?我该怎么做才能让它起作用?

const std::string& getVal(const std::string &key) {
  boost::shared_ptr<std::string> retVal(new std::string());
  ... //build retVal string with += operator based on key
  return *retVal;
}
6个回答

3

在C++中,无法从函数返回对局部变量的引用。虽然在C++0x中这是可能的。

将字符串分配到堆上并手动清理:

如果您不能更改函数的接口,则需要在堆上创建它,然后在使用后手动删除它。

//Remember to free the address that getVal returns
const std::string& getVal(const std::string &key) {
  std::string *retVal = new std::string();
  ... //build retVal string with += operator based on key
  return *retVal;
}

同样的解决方案但不是手动操作:

由于如果您忘记手动释放内存,上述方法最终会导致内存泄漏。我建议将此调用封装到一个类中并使用RAII。即在构造函数中调用getVal,并将该类的成员设置为指向它的指针。在您的类的析构函数中,您应该删除它。

为什么您提供的带有shared_ptr的代码不起作用:

shared_ptr通过引用计数工作。由于您正在销毁唯一的shared_ptr对象(通过作用域),因此没有剩余的引用,内存将被释放。要使其起作用,您必须返回一个shared_ptr,但您说您无法这样做。


2
你需要返回一个boost shared_ptr,而不是std::string。一旦函数退出,栈上的shared_ptr将超出作用域,并且由于只有一个引用,它将被删除。你还需要返回一个副本而不是引用。
永远不要返回指向堆栈变量的引用,因为一旦函数存在,它就会被删除。
如果你不能改变返回类型,那么唯一(令人讨厌的)选项是在堆上分配字符串,返回指针的引用,并确保调用代码知道它是一个指针,并稍后删除它。
例如:
const std::string& getVal(const std::string &key) {
  return *(new std::string("Something"));
}

// get the val, remember we must delete this pointer!
std::string* pString = &getVal("SomeKey");
delete pString;

我不确定删除getVal结果的地址是否合法。 - Evan Teran
如果返回的是在堆上分配的引用,那么这是合法的。 - Andrew Grant

2

这不是boost问题。你不能返回一个局部变量的引用,因为一旦函数终止,该变量就会消失。在这种情况下,你应该返回一个值。然而,你可以使用一个技巧,即将局部变量设置为静态的,但这可能会导致并发和其他问题。


0

由于您无法更改函数的签名,因此可以返回对静态变量的引用:

const std::string& getVal(const std::string &key) {
    static std::string retVal;
    ... //build retVal string with += operator based on key
    return retVal;
}

然而,这种方法存在严重问题。它本质上不是线程安全的。即使您的应用程序不是多线程的,根据调用者保留引用的时间长短,它也可能会导致问题。

如果您的应用程序不是多线程的,并且每次调用getVal()都立即使用或复制字符串,则您可能可以使用此方法。但我强烈建议只返回一个std::string。


不确定如何以-1结束。现有的签名已经说明返回一个参考文献。线程和生命周期问题源于该参考文献和签名。使用函数静态不会使它更糟。 - MSalters

0
另一个需要考虑的问题是,如果可能的话,标准允许编译器优化掉复制。标准称之为“拷贝省略”,但大多数人知道它是“命名返回值优化”,请参见此处
基本概念是,在可能的情况下,编译器将直接构造局部对象到函数调用结果的对象上:
class A {};

A foo () {
  A a;          // #1
  return a;
}

void bar () {
  A b = foo();  // #2
}

在上面的例子中,'a'将在bar中与'b'相同的位置构建,因此当函数返回时根本不需要复制。总之,值得测试编译器以查看它是否执行此优化。如果您感兴趣,可以参考12.8/15的标准参考资料。

0

由于您需要的是const引用,因此您可以通过值返回并使调用者使用const引用接收它。

Herb Sutter GotW 88


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接