C++ std::string寫時複製與深淺拷貝
阿新 • • 發佈:2020-12-09
很久以前就瞭解過std::string的寫時複製(copy on write)優化,但和深淺拷貝放到一起的時候,就不是那麼直截了當了。
std::string到底是深拷貝還是淺拷貝呢?網上兩種說法都有,我的理解是:深拷貝。
// copy on write static void TestStringCopyCase1() { std::string a = "Hello World"; std::string b = a; printf("pointer of a: %p\n", a.c_str()); printf("pointer of b: %p\n", b.c_str()); }// copy on write static void TestStringCopyCase2() { std::string a = "Hello World"; std::string b = a; printf("pointer of a: %p\n", a.c_str()); printf("pointer of b: %p\n", b.c_str()); b[0] = 'h'; // b += "!"; printf("pointer of a: %p\n", a.c_str()); printf("pointer of b: %p\n", b.c_str()); std::cout << a << std::endl; std::cout << b << std::endl; } // output: pointer of a: 0x1144028 pointer of b: 0x1144028
pointer of a: 0x1144028 pointer of b: 0x1144028 pointer of a: 0x1144028 pointer of b: 0x1144058 Hello World hello World
這兩個case很明確地證明std::string是深拷貝的,對副本的修改不會影響到原件。只不過,在修改副本之前,它們的c_str()指標是指向同一地址的,只有在嘗試寫入的時候,才會區分開來。
具體的實現方法,是對資料塊進行了引用計數,嘗試修改的時候,引用計數不為1,就要複製再修改。
那麼這裡就隱藏了一個空子,如果繞過引用計數,直接修改原始資料,會怎樣?
// misuse: modify b, but a is effected. static void TestStringCopyCase3() { std::string a = "Hello World"; std::string b = a; char* char_array = (char *)b.c_str(); char_array[0] = 'h'; printf("pointer of a: %p\n", a.c_str()); printf("pointer of b: %p\n", b.c_str()); std::cout << a << std::endl; std::cout << b << std::endl; } // output: pointer of a: 0x1144028 pointer of b: 0x1144028 hello World hello World
修改副本,導致原件也被修改。是一個容易引起錯誤的地方。
如何避免呢?
// deep copy to avoid misuse static void TestStringCopyCase4() { std::string a = "Hello World"; std::string b = a.c_str(); // deep copy char* char_array = (char *)b.c_str(); char_array[0] = 'h'; printf("pointer of a: %p\n", a.c_str()); printf("pointer of b: %p\n", b.c_str()); std::cout << a << std::endl; std::cout << b << std::endl; } // output: pointer of a: 0x1144028 pointer of b: 0x1144058 Hello World hello World
複製的時候,直接複製源資料,繞開寫時複製。這就給人一種錯覺,好像std::string的拷貝函式是淺拷貝,需要刻意深拷貝。
結論:
如果使用std::string本身的成員函式或者操作符來操作std::string,它本身就是深拷貝的;
如果使用指標直接操作std::string源資料,會繞過“寫時複製”機制,需要主動deep copy,以避免資料誤寫。