簡體   English   中英

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

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

我有一節課說,

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

或者,另一種方式可以是將每個函數傳遞給std::string緩沖區,就像上面第一個例子中所示

兩種方式似乎有點難看的變通方法,能夠使用std::string而不復制,因為我很少看到std :: string用作指針或傳遞類的相同參數的所有函數。

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

在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&參數。 這樣可以避免副本,但會給調用者留下組織正確管理字符串的責任。

你基本上需要回答這個問題:誰擁有字符串? Foo擁有字符串嗎? 外部呼叫者是否擁有該字符串? 或者他們都共享字符串的所有權。

“擁有”字符串意味着字符串的生命周期與它相關聯。 因此,如果Foo擁有該字符串,則當Foo停止存在或銷毀它時,該字符串將停止存在。 共享所有權要復雜得多,但我們可以通過說只要任何所有者保留字符串就可以使字符串更簡單。

每種情況都有不同的答案:

  1. Foo擁有字符串:將字符串復制到Foo ,然后讓成員方法改變它。
  2. 外部資源擁有字符串: Foo永遠不應該在其自己的堆棧之外保存對字符串的引用,因為字符串可能在不知情的情況下被銷毀。 這意味着它需要通過引用傳遞給使用它的每個方法並且不擁有它,即使方法在同一個類中也是如此。
  3. 共享所有權:在創建字符串時使用shared_ptr ,然后將該shared_ptr傳遞給共享所有權的每個實例。 然后,將shared_ptr復制到成員變量,然后方法訪問它。 這比通過引用傳遞更高的開銷,但如果您想要共享所有權,這是最安全的方法之一。

實際上有幾種其他方式可以模擬所有權,但它們往往更為深奧。 所有權薄弱,可轉讓所有權等

既然你的要求是那樣的話

1.我不想傳遞我已經擁有的std :: string的副本。

2.我不想創建這個類的多個對象。

使用pass by ref將是解決方案1使用靜態將是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);

更進一步,如果您只想要此類的一個實例,則可以參考單例設計模式。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM