【问题标题】:C++: use std::string returned by a function: Using pointer to local variable that is out of scopeC ++:使用函数返回的std :: string:使用指向超出范围的局部变量的指针
【发布时间】:2021-12-18 20:49:26
【问题描述】:

我有以下功能:

MyFunction(const char *value, bool trigger) {
    if (trigger) {
        std::string temporaryString = getTemporaryStringFromSomewhereElse();
        value = temporaryString.c_str();
    }
    // do processing here
    // I need `value` and `temporaryString.c_str();` to be alive and accessible here and not destroyed
 
    MyClass *object = new MyClass(value);
    object->Work();
    // etc..

}

那么,问题是,如何在if 子句的范围之外“延长”temporaryString 的生命周期?

目前,我收到以下错误:

使用指向超出范围的局部变量“临时”的指针。

我知道这与内存管理有关,并且temporaryif 子句之后被假定为“销毁”或从内存中清除。但我需要延长它的生命周期,或者创建另一个字符串(副本),它具有不同的、更广泛的范围。

我怎样才能做到这一点?

要求:

  1. 函数签名不能改变,它应该保持:MyFunction(const char *value, bool trigger)
  2. 我稍后使用value 来初始化另一个对象,做一些其他的工作。我不能有 2 个变量,例如valueanotherValueToBeUsedIfTriggerIsTrue

【问题讨论】:

  • 为什么不简单地在块之前定义你的字符串并在块内设置值?
  • 是否有一个选项可以完全摆脱temporaryString并直接分配给vaule
  • 如果您需要“在块内设置”的信息,您也可以使用std::optional。在我看来,这是最易读的代码
  • 这听起来很有趣,您能否修改我的示例以展示如何使用它?在实践中,我只需要将值设置为一个新值,就是这样。当然,它应该可以在区块之外访问。
  • 看看我回答的变化...

标签: c++ memory memory-management scope stdstring


【解决方案1】:

只需将std::string 的声明移出if 块,向上移动到功能块中,例如:

MyFunction(const char *value, bool trigger) {
    std::string temporaryString;

    if (trigger) {
        temporaryString = getTemporaryStringFromSomewhereElse();
        value = temporaryString.c_str();
    }

    // do processing here
 
    MyClass *object = new MyClass(value);
    object->Work();
    // etc..

}

std::string 最初为空白,并在函数退出时销毁,因此重新分配的 value 在函数运行期间将保持有效,只要 temporaryString 未被修改。

【讨论】:

  • 我最终得到了这个解决方案
【解决方案2】:

在这种情况下我更喜欢std::optional,因为它还会显示请求的对象是否已设置。

例子:

std::string getTemporaryStringFromSomewhereElse()
{
    return "Here I am";
}

std::optional< std::string > MyFunction(bool trigger) {
    if (trigger) {
        return getTemporaryStringFromSomewhereElse();
    }   

    return std::nullopt;
}

int main()
{
    auto retval = MyFunction( true );
    if ( retval )
    {   
        std::cout << *retval << std::endl;
    }   
}

编辑:(在我们得到界面不可更改的信息之后)

这最终涉及所有权问题!

谁将分配传递的字符串的内存,谁负责释放该内存。

选项:

  1. 分配足够的空间并将指向该内存的指针传递给函数并在设置触发器或将内存的第一个字符设置为零时复制内容以显示您传回一个空字符串。如果也可以使用空字符串,请使用例如第一个或最后一个内存元素作为标志。
  2. 在函数内部分配内存并复制给定的字符串并传回指针,如果未设置触发器,则返回 nullptr。函数的调用者必须处理释放给定内存的问题。
  3. 如果保证在第一次调用和使用内容之间不会再次调用该函数,则该函数本身可以保留一个静态数组并将地址传回该静态内存.

顺便说一句:保留损坏的接口是不良软件的一个很好的起点 :-) 在给定的情况下,如果空字符串也是有效的,您开始使用数组中的标志和所有损坏的东西(通常我们应该使用带有标志的结构在其中已经用 std::optional 定义)。

【讨论】:

  • 请注意,函数签名不能更改。我需要它是MyFunction(const char *value, bool trigger)
  • @RichardTopchii 这应该是您问题的一部分!
  • 现在是,我已经更新了我的问题。
  • 实现这个的最简单/最直接的方法是什么?看起来选项 1 是最简单的。在第一次调用和使用内容之间不会再次调用该函数。到目前为止,我已经结束在 if 子句范围之外提取 temporaryString 声明,它似乎已经解决了这个问题,虽然代码不是很优雅。
【解决方案3】:

static 存储可能是您正在寻找的。 变量的static 属性将其生命周期延长到整个程序执行时间。

void MyFunction(const char *value, bool trigger) {
    if (trigger) {
        static std::string s_buffer; // Note : this line is executed only once so don't assign the value here
        s_buffer = getTemporaryStringFromSomewhereElse();
        value = s_buffer.c_str();
    }
    // use of value is still correct even outside the if statement.
}

注意:在多线程程序的情况下,使用 static 不是线程安全的,为此标准提供了 thread_local 存储。

【讨论】:

  • 一旦MyFunction 返回,static std::string s_buffer; 会被释放吗?
  • @RichardTopchii 不,这就是重点。在static 存储中,string 将在程序生命周期的剩余时间内存在。对MyFunction 的每次调用都将在同一个string 对象上运行。这也是为什么代码不是线程安全的,如果多个线程同时调用MyFunction()
猜你喜欢
  • 2020-10-13
  • 1970-01-01
  • 2023-04-09
  • 1970-01-01
  • 1970-01-01
  • 2013-10-03
  • 2016-10-25
相关资源
最近更新 更多