【问题标题】:Move constructor for std::string from char*从 char* 移动 std::string 的构造函数
【发布时间】:2015-04-26 22:45:35
【问题描述】:

我有一个函数 f 返回一个 char*。功能文档说:

The user must delete returned string

我想从中构造一个std::string。琐碎的事情是:

char* cstring = f();
std::string s(cstring);
delete cstring;

是否可以使用 C++ 功能做得更好?我想写一些类似的东西

std::string(cstring)

避免泄漏。

【问题讨论】:

  • 你可以将f()的结果放入unique_ptr,然后用它来初始化字符串。您仍然需要两行代码。你应该向写f()的人投诉。
  • 并抽象出转换。
  • @juanchopanza:为什么是两行代码? std::string(std::unique_ptr<char[]>(f()).get()) 怎么了?
  • @BenVoigt Ha!是的,当然,那会很好(尽管有些人可能会反对发生了太多事情。)
  • @juanchopanza:当然,在语句块中,分成多个更简单的步骤是有意义的。但是单线在没有阻塞的情况下非常方便,例如初始化器和单个表达式 lambdas。

标签: c++ string c++11 move-semantics


【解决方案1】:

std::string 将复制以 null 结尾的字符串参数并管理该副本。没有办法让它拥有你传递给它的字符串的所有权。所以你所做的是正确的,我建议的唯一改进是检查nullptr,假设这是f() 的有效返回值。这是必要的,因为采用char const *std::string 构造函数要求参数指向一个有效的数组,而不是nullptr

char* cstring = f();
std::string s(cstring ? cstring : "");
delete[] cstring;   // You most likely want delete[] and not delete

现在,如果您不需要std::string 的所有接口,或者如果避免复制很重要,那么您可以使用unique_ptr 来管理字符串。

std::unique_ptr<char[]> s{f()}; // will call delete[] automatically

您可以通过s.get() 访问托管char *,当s 超出范围时,字符串将为deleted。

即使您选择第一个选项,我建议在将f() 的返回值传递给std::string 构造函数之前将其存储在unique_ptr 中。这样如果构造抛出,返回的字符串仍然会被删除。

【讨论】:

  • 嗯。如果std::string-ctor 抛出,无论如何都应该使用unique_ptr 以避免泄漏。
【解决方案2】:

std::string 没有标准方法来获取您传递的缓冲区的所有权。

也不负责清理这样的缓冲区。

理论上,一个知道所有内部细节的实现,可以std::string添加一种方法来接管分配器分配的缓冲区,但我不知道有任何实现确实。
也不能保证这样做实际上是有利的,具体取决于实现细节。

【讨论】:

    【解决方案3】:

    这段代码永远不会是正确的:

    std::string s(cstring);
    delete cstring;
    

    采用字符指针的std::string 构造函数需要一个以 NUL 结尾的字符串。所以是多个个字符。

    delete cstring 是标量删除。

    您要么尝试从字符标量创建字符串(在这种情况下,为什么要间接?)

    std::string s(cstring[0]);
    delete cstring;
    

    或者你有多个字符,应该相应地删除

    std::string s(cstring);
    delete [] cstring;
    

    检查其他答案以确保使用delete[] 的推荐方式,例如

    std::string(std::unique_ptr<char[]>(f()).get())
    

    【讨论】:

    • 这就是 cmets 应该允许代码的原因。这是一个重要的观点,但不是答案。
    • @MobyDisk:确实。另一方面,点赞按钮显示“这很有用”,而不是“这是对问题各个方面的完整答案”。
    • 同意。是否有关于该主题的元讨论?过去,我因没有回答整个问题而被与你有类似代表的人投票给下地狱。
    • unique_ptr(char*)0 构造时,将在.get() 上返回null。 std::string(char const*)0 构造时会执行我认为的未定义行为?因此,您帖子末尾的构造似乎过于危险。
    • @Yakk:嗯,问题std::string s(cstring); 中的那行说它不能
    【解决方案4】:
    std::string steal_char_buffer( std::unique_ptr<char[]> buff ) {
      std::string s = buff?buff.get():""; // handle null pointers
      return s;
    }
    std::string steal_char_buffer( const char* str ) {
      std::unique_ptr<char[]> buff(str); // manage lifetime
      return steal_char_buffer(std::move(buff));
    }
    

    现在你可以输入

    std::string s = steal_char_buffer(f());
    

    你会从f() 中得到一个std::string

    可能想让steal_char_buffer 的参数成为const char*&amp;&amp;。这大多是没有意义的,但它可能会导致一些有用的错误。

    如果你可以改变f的接口,让它直接返回一个std::string或者一个std::unique_ptr&lt;char[]&gt;

    另一个好主意是将f 包装在另一个返回std::unique_ptr&lt;char[]&gt;std::string 的函数中:

    std::unique_ptr<char[]> good_f() {
      return std::unique_ptr<char[]>(f());
    }
    

    和/或

    std::string good_f2() {
      auto s = good_f();
      return steal_char_buffer( std::move(s) );
    }
    

    【讨论】:

    • 不会复制缓冲区然后删除原来的吗?我不认为这就是问题所在。他要专门避免复制和删除。
    • @MobyDisk 我认为 OP 想要避免不得不记住删除的丑陋语法(额外的噪音)
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2023-03-16
    • 1970-01-01
    • 2011-11-25
    • 2015-03-16
    相关资源
    最近更新 更多