如何在C ++中将const ref a返回给局部变量?

时间:2009-03-11 23:21:34

标签: c++ boost

我有一个功能,我无法更改功能参数。我需要返回一个const引用到这个函数中创建的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 个答案:

答案 0 :(得分: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,但你说你不能这样做。

答案 1 :(得分:3)

您需要返回boost shared_ptr,而不是std :: string。一旦函数退出,堆栈上的shared_ptr将超出范围,因为只有一个引用它将被删除。您还需要返回副本而不是引用。

你永远不应该返回对堆栈变量的引用,因为只要该函数存在,它就会被删除。

如果你不能改变返回类型那么唯一的(icky)选项是在堆上分配字符串,返回对指针的引用,并确保调用代码知道它是一个指针,然后删除它。 / p>

E.g。

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;

答案 2 :(得分:2)

这不是一个提升问题。您不能返回对局部变量的引用,因为一旦函数终止,该变量将不再存在。在这种情况下,你应该真的返回一个值。但是,您可以使用的一个方法是将局部变量设置为静态,但这可能会导致并发性和其他问题。

答案 3 :(得分: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。

答案 4 :(得分:0)

因为你想要的是const引用,你可以按值返回并让调用者通过const引用接收它。

Herb Sutter GotW 88

答案 5 :(得分:0)

需要考虑的另一点是,标准允许编译器在可能的情况下优化副本。标准是指 copy elision ,但大多数人都知道它为“命名返回值优化”,请参阅here

基本概念是,在可能的情况下,编译器将直接在作为函数调用结果的对象上构造本地对象:

class A {};

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

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

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