简体   繁体   English

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

[英]How to use a std::string without copying?

I have a class say, 我有一节课说,

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
      }
};

This class tries to use a std::string buffer to perform operations on it using various methods under these conditions: 在这些条件下,此类尝试使用std::string缓冲区对其执行操作:

  • I don't want to pass a copy of std::string which I already have. 我不想传递我已经拥有的std::string的副本。
  • I don't want to create multiple objects of this class. 我不想创建这个类的多个对象。

For example: 例如:

// 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);

I could use the string and assign it as a class member so that other functions using can know about it. 我可以使用该字符串并将其指定为类成员,以便其他使用的函数可以知道它。

But it seems we cannot have a reference class member std::string &buffer because it can only be initialized from constructor. 但似乎我们不能有引用类成员std::string &buffer因为它只能从构造函数初始化。

I could use a pointer to std::string ie std::string *buffer and use it as a class member and then pass the addresses of s1, s2, s3 . 我可以使用指向std::string ie 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;
};

Or, the other way could be pass each functions a reference to std::string buffer just as shown in the first example above . 或者,另一种方式可以是将每个函数传递给std::string缓冲区,就像上面第一个例子中所示

Both ways kind of seem a bit ugly workarounds to be able to use a std::string without copying as I have rarely seen the usage of std::string as a pointer or pass all the functions of class the same argument. 两种方式似乎有点难看的变通方法,能够使用std::string而不复制,因为我很少看到std :: string用作指针或传递类的相同参数的所有函数。

Is there a better around this or what I'm doing is just fine? 周围有没有更好的,或者我正在做什么就好了?

Keeping in MyObject a reference or a pointer to a string which is not ownned by your object is dangerous. 在MyObject中保存一个引用或指向不受对象拥有的字符串的指针是危险的。 It will be easy to get nasty undefined behaviour . 很容易得到讨厌的未定义行为

Look at the following legal example (Bar is public): 请看以下法律示例(Bar是公开的):

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

Look at the following UB: 看看下面的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                              

The errors are very nasty, because when reading function's usage, everything seems ok and well managed. 错误是非常讨厌的,因为在阅读功能的使用时,一切似乎都很好并且管理得很好。 The problem is hidden by automatic destruction of bloc variables. 通过自动销毁bloc变量隐藏了这个问题。

So if you really want to avoid the copy of the string, you could use a pointer as you envisaged, but you shall only use this pointer in functions called directly by ProcessString(), and make these functions private. 因此,如果你真的想要避免字符串的副本,你可以使用你设想的指针,但是你只能在ProcessString()直接调用的函数中使用这个指针,并使这些函数变为私有。

In all other case, I'd strongly suggest to reconsider your position, and envisage: 在所有其他情况下,我强烈建议重新考虑你的立场,并设想:

  • a local copy of the string in the object that shall use it. 应该使用它的对象中的字符串的本地副本。
  • Or use a string& parameters in all the object's function that need it. 或者在需要它的所有对象函数中使用string&参数。 This avoids the copies but leaves to caller the responsibility of organising the proper management of the string. 这样可以避免副本,但会给调用者留下组织正确管理字符串的责任。

You basically need to answer this question: who owns the string? 你基本上需要回答这个问题:谁拥有字符串? Does Foo own the string? Foo拥有字符串吗? Does the external caller own the string? 外部呼叫者是否拥有该字符串? Or do they both share ownership of the string. 或者他们都共享字符串的所有权。

"Owning" the string means that the lifetime of the string is tied to it. “拥有”字符串意味着字符串的生命周期与它相关联。 So if Foo owns the string, the string will stop existing when Foo stops existing or destroys it. 因此,如果Foo拥有该字符串,则当Foo停止存在或销毁它时,该字符串将停止存在。 Shared ownership is far more complicated, but we can make it simpler by saying that the string will exist as long as any of the owners keep it. 共享所有权要复杂得多,但我们可以通过说只要任何所有者保留字符串就可以使字符串更简单。

Each situation has a different answer: 每种情况都有不同的答案:

  1. Foo owns the string: Copy the string into Foo , then let the member methods mutate it. Foo拥有字符串:将字符串复制到Foo ,然后让成员方法改变它。
  2. External resource owns the string: Foo should never hold a reference to the string outside of its own stack, since the string could be destroyed without its knowledge. 外部资源拥有字符串: Foo永远不应该在其自己的堆栈之外保存对字符串的引用,因为字符串可能在不知情的情况下被销毁。 This means that it needs to be passed by reference to every method that uses it and does not own it, even if the methods are in the same class. 这意味着它需要通过引用传递给使用它的每个方法并且不拥有它,即使方法在同一个类中也是如此。
  3. Shared ownership: Use a shared_ptr when creating the string, then pass that shared_ptr to every instance that shares ownership. 共享所有权:在创建字符串时使用shared_ptr ,然后将该shared_ptr传递给共享所有权的每个实例。 You then copy the shared_ptr to a member variable, and methods access it. 然后,将shared_ptr复制到成员变量,然后方法访问它。 This has much higher overhead then passing by reference, but if you want shared ownership it is one of the safest ways to do so. 这比通过引用传递更高的开销,但如果您想要共享所有权,这是最安全的方法之一。

There are actually several other kinds of ways to model ownership, but they tend to be more esoteric. 实际上有几种其他方式可以模拟所有权,但它们往往更为深奥。 Weak ownership, transferable ownership, etc. 所有权薄弱,可转让所有权等

Since your requirement is that 既然你的要求是那样的话

1.I don't want to pass a copy of std::string which I already have. 1.我不想传递我已经拥有的std :: string的副本。

2.I don't want to create multiple objects of this class. 2.我不想创建这个类的多个对象。

using pass by ref would be the solution to 1 using static would be the solution to 2. since it is a static memeber method, there would be only one copy of this method. 使用pass by ref将是解决方案1使用静态将是2的解决方案。因为它是一个静态的memeber方法,所以只有这个方法的一个副本。 it wont belong to any object, though. 但它不属于任何物体。 With that being said, you can call this method directly instead of through an object. 话虽如此,您可以直接调用此方法,而不是通过对象。

For example, 例如,

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

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

}

when you call this method, it would be something like this: 当你调用这个方法时,它会是这样的:

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

One step further, if you want only one instance of this class, you can refer to singleton design pattern. 更进一步,如果您只想要此类的一个实例,则可以参考单例设计模式。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM