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

时间:2014-08-23 16:04:00

标签: 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
      }
};

此类尝试使用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);

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

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

我可以使用指向std::string的指针,即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;
};

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

两种方式似乎都有点丑陋的变通方法能够使用std::string而无需复制,因为我很少看到使用std :: string作为指针或传递类的所有函数相同的参数

这周围有更好的或我正在做的事情还不错吗?

3 个答案:

答案 0 :(得分:7)

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

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

myObject.ProcessString(s1);     // start with s1 and keep its address
myObject.Bar();                 // works with s1 (using address previously stored) 

请看以下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                              

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

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

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

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

答案 1 :(得分:6)

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

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

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

  1. Foo拥有字符串:将字符串复制到Foo,然后让成员方法改变它。
  2. 外部资源拥有字符串:Foo永远不应该在其自己的堆栈之外保存对字符串的引用,因为字符串可能在不知情的情况下被销毁。这意味着它需要通过引用传递给每个使用它的方法,并且不拥有它,即使这些方法属于同一个类。
  3. 共享所有权:在创建字符串时使用shared_ptr,然后将该shared_ptr传递给共享所有权的每个实例。然后,将shared_ptr复制到成员变量,然后方法访问它。这比通过引用传递的开销高得多,但是如果你想要共享所有权,那么这是最安全的方法之一。
  4. 实际上还有其他几种方法来模拟所有权,但它们往往更为深奥。所有权薄弱,可转让所有权等。

答案 2 :(得分:0)

因为您的要求是

  

1.我不想传递我已经拥有的std :: string的副本。

     

2.我不想创建这个类的多个对象。

使用pass by ref将是1的解决方案 使用static将是2的解决方案。因为它是一个静态的memeber方法,所以只有这个方法的一个副本。但它不属于任何物体。话虽如此,您可以直接调用此方法,而不是通过对象。

例如,

class Foo
{
      static void ProcessString(std::string &s)
      {
          // perform operations on std::string

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

}

当你调用这个方法时,它会是这样的:

std::string s1, s2, s3;
Foo::ProcessString(s1);
Foo::ProcessString(s2);
Foo::ProcessString(s3);

更进一步,如果你只想要这个类的一个实例,你可以参考单例设计模式。