如何使用std :: string而不复制?

cpx*_*cpx 7 c++ string stl parameter-passing pass-by-reference

我有一节课说,

class Foo
{
   public:
      void ProcessString(std::string &buffer)
      {
          // perform operations on std::string

          // call other functions within class
          // which use same std::string string
      }

      void Bar(std::string &buffer)
      {
          // perform other operations on "std::string" buffer
      }

      void Baz(std::string &buffer)
      {
          // perform other operations on "std::string" buffer
      }
};
Run Code Online (Sandbox Code Playgroud)

std::string在这些条件下,此类尝试使用缓冲区对其执行操作:

  • 我不想传递std::string我已经拥有的副本.
  • 我不想创建这个类的多个对象.

例如:

// Once an object is created
Foo myObject;

// We could pass many different std::string's to same method without copying
std::string s1, s2, s3;
myObject.ProcessString(s1);
myObject.ProcessString(s2);
myObject.ProcessString(s3);
Run Code Online (Sandbox Code Playgroud)

我可以使用该字符串并将其指定为类成员,以便其他使用的函数可以知道它.

但似乎我们不能拥有引用类成员,std::string &buffer因为它只能从构造函数初始化.

我可以使用指向std::stringie 的指针,std::string *buffer并将其用作类成员,然后传递地址s1, s2, s3.

class Foo
{
   public:
      void ProcessString(std::string *buf)
      {
          // Save pointer
          buffer = buf;

          // perform operations on std::string

          // call other functions within class
          // which use same std::string string
      }

      void Bar()
      {
          // perform other operations on "std::string" buffer
      }

      void Baz()
      {
          // perform other operations on "std::string" buffer
      }
   private:
       std::string *buffer;
};
Run Code Online (Sandbox Code Playgroud)

或者,另一种方式可以是将每个函数传递给std::string缓冲区,就像上面第一个例子中所示.

两种方式似乎有点难看的变通方法,std::string因为我很少看到使用std :: string作为指针或传递类的相同参数的所有函数.

周围有没有更好的,或者我正在做什么就好了?

Chr*_*phe 7

在MyObject中保存一个引用或指向不受对象拥有的字符串的指针是危险的.很容易得到讨厌的未定义行为.

请看以下法律示例(Bar是公开的):

myObject.ProcessString(s1);     // start with s1 and keep its address
myObject.Bar();                 // works with s1 (using address previously stored) 
Run Code Online (Sandbox Code Playgroud)

看看下面的UB:

if (is_today) {
    myObject.ProcessString(string("Hello"));  // uses an automatic temporary string
}                                             // !! end of block: temporary is destroyed!
else {
    string tmp = to_string(1234);            // create a block variable 
    myObject.ProcessString(tmp);             // call the main function 
}                                            // !! end of block:  tmp is destroyed
myObject.Bar();  // expects to work with pointer, but in reality use an object that was already destroyed !!  => UB                              
Run Code Online (Sandbox Code Playgroud)

错误是非常讨厌的,因为在阅读功能的使用时,一切似乎都很好并且管理得很好.通过自动销毁bloc变量隐藏了这个问题.

因此,如果你真的想要避免字符串的副本,你可以使用你设想的指针,但是你只能在ProcessString()直接调用的函数中使用这个指针,并使这些函数成为私有的.

在所有其他情况下,我强烈建议重新考虑你的立场,并设想:

  • 应该使用它的对象中的字符串的本地副本.
  • 或者string&在需要它的所有对象函数中使用参数.这样可以避免副本,但会给调用者留下组织正确管理字符串的责任.


Chr*_*man 6

你基本上需要回答这个问题:谁拥有字符串?是否Foo拥有该字符串?外部呼叫者是否拥有该字符串?或者他们都共享字符串的所有权.

"拥有"字符串意味着字符串的生命周期与它相关联.因此,如果Foo拥有该字符串,当Foo停止存在或销毁它时,该字符串将停止存在.共享所有权要复杂得多,但我们可以通过说只要任何所有者保留字符串就可以使字符串更简单.

每种情况都有不同的答案:

  1. Foo拥有字符串:将字符串复制到Foo,然后让成员方法改变它.
  2. 外部资源拥有字符串:Foo永远不应该在其自己的堆栈之外保存对字符串的引用,因为字符串可能在不知情的情况下被销毁.这意味着它需要通过引用传递给每个使用它的方法并且不拥有它,即使方法在同一个类中也是如此.
  3. 共享所有权:shared_ptr在创建字符串时使用a ,然后将该shared_ptr传递给共享所有权的每个实例.然后,将shared_ptr复制到成员变量,然后方法访问它.这通过引用传递的开销要高得多,但如果您想要共享所有权,那么这是最安全的方法之一.

实际上有几种其他方式可以模拟所有权,但它们往往更为深奥.所有权薄弱,可转让所有权等