【问题标题】:Function for both C-style strings and c++ std::stringC 风格字符串和 c++ std::string 的函数
【发布时间】:2013-11-20 21:28:57
【问题描述】:

我有一个操作字符串的函数,我需要它同时处理 C 样式字符串和 C++ std::string:

// C-style overload
void TransformString(const char *in_c_string, char *out_string);
// C++ std::strings overload
std::string TransformString(const std::string &in_string);

为了避免冗余代码,我可以只在其中一个中实现实际算法,然后让另一个调用它。因此,如果将实现放在 C++ 重载函数中,那么 C 风格的函数将如下所示:

void TransformString(const char *in_c_string, char * out_c_string) {
   std::string in_string(in_c_string);
   std::string out_string = TransformString(in_string); // call C++ std::string overload
   strcpy(out_c_string, out_string.c_str()); // unwanted memory copy
}

我的问题是:我可以在没有额外副本(从std::string 内部缓冲区到 C 样式字符串)的情况下执行此操作(仅在一个函数中实现算法)吗?我的第一个想法是尝试“窃取”缓冲区,就像字符串移动构造函数一样,但是在搜索网络时,似乎没有安全的方法可以做到这一点,因为它是特定于实现的。如果我在 C 风格的函数中编写算法,问题与在 C++ 函数中的问题相同,我必须为 char* 字符串分配空间,然后将其移动到 std::string 对象。
必须提一下,在转换完成之前我不知道结果字符串的大小。

谢谢。

编辑

缓冲区的大小在这里不是问题(我知道最大大小并且函数接收分配的缓冲区)。 我不能只返回 std::string.c_str() ,因为当 std::string 对象被销毁时(就在返回发生之后),缓冲区将变得无效。 我已更改变量 out_c_string 的名称。 (感谢 0x499602D2)

【问题讨论】:

  • 由于事先不知道输出大小,调用者无法为结果分配内存。然后我看不出你的 C 风格的重载是如何工作的。您需要 char **out_string 作为第二个参数(或者只返回 char *
  • 您的示例中有两个名为 out_string 的变量。
  • 你能不能只写std::string的代码,然后从c风格的字符串函数中调用那个方法并在返回值上使用std::string.c_str()
  • 我不知道实际大小,但我知道最大大小并且函数接收分配的缓冲区。
  • 我不能只返回 std::string.c_str() 因为当 std::string 对象被销毁时缓冲区会失效

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


【解决方案1】:

只要您知道输出缓冲区需要多大,您就可以创建一个std::string 并将其调整为缓冲区大小。然后,您可以将指向 std::string 缓冲区的指针传递给 C 样式的重载。

#include <cstring>
#include <iostream>
#include <string>

void TransformString(const char *in_c_string, char *out_c_string) {
    size_t length = strlen(in_c_string);

    for (size_t i = 0; i < length; ++i)
        out_c_string[i] = '*';

    out_c_string[length] = 'a';
    out_c_string[length+1] = 'b';
    out_c_string[length+2] = 'c';
    out_c_string[length+3] = '\0';
}

std::string TransformString(const std::string &in_string) {
    std::string out;
    out.resize(100);

    TransformString(in_string.c_str(), &out[0]);
    out.resize(strlen(&out[0]));

    // IIRC there are some C++11 rule that allows 'out' to
    // be automatically moved here (if it isn't RVO'd)
    return out;
}

int main() {
    std::string string_out = TransformString("hello world");

    char charstar_out[100];
    TransformString("hello world", charstar_out);

    std::cout << string_out << "\n";
    std::cout << charstar_out << "\n";

    return 0;
}

这是一个活生生的例子:http://ideone.com/xwVWCh

【讨论】:

  • 这与 c_str 提案有相同的问题:修改 std::string 的内部数据是 UB。 (它“似乎工作”的事实是无关紧要的。)
  • @rici 我不认为这是 UB。请参阅圣典中的 21.4.1.5 和 21.4.5。
  • 我不相信。 21.4.5 允许您通过返回的引用修改字符数组,但它仍然允许 std::string 实现使用记录发生修改的事实的引用类型。 21.4.7.1 禁止使用data() 返回的指针修改字符数组。如果可以通过指针修改字符数组,21.4.7.1的禁止点是什么?
  • @rici 即使字符串使用了代理引用类型,它仍然应该尊重编辑,所以我没有看到问题。至于缺少非constdata() 功能,看来这是一个疏忽:stackoverflow.com/questions/7518732/…
  • @rici: 21.4.5 不排除通过operator[]() 进行修改,除非size() 用作索引(即终止空字符)。请参阅stackoverflow.com/questions/7766087/… 请务必阅读两个答案。
【解决方案2】:

您可以尝试使用c_str() 从字符串类中获取 c 样式字符串。您必须执行 const_cast&lt;char*&gt; 才能删除 const。

这仅在您不需要重新分配字符串(保持相同大小)时才有效。

【讨论】:

  • 那是 UB,它将对执行写时复制的标准库的实现产生潜在的灾难性影响(尽管这些库比以前少了)。
  • 否,因为当 std::string 对象被销毁时缓冲区会失效。
猜你喜欢
  • 2020-04-16
  • 2011-06-13
  • 2016-05-08
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2012-06-13
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多