2011-12-05 54 views
13

在我的一個C++項目中,我是用std::string替換所有char*之前的一個步驟,但我發現std::string失敗的一個特定情況。C++字符串對字符串文字不夠優化

想象我有這2個功能:

void foo1(const std::string& s) 
{ 
    ... 
} 

void foo2(const char* s) 
{ 
    ... 
} 

如果我寫的是這樣的:

const char* SL = "Hello to all!"; 

foo1(SL); // calls malloc, memcpy, free 
foo2(SL); 

foo1SL將隱式轉換爲std::string。這意味着std::string構造函數將分配內存,並且它會將字符串文字複製到該緩衝區。在foo2雖然沒有這些會發生。

在大多數實現中,std::string應該是超級優化的(例如寫入時拷貝),但是當我使用const char*構建時,它不是。我的問題是:爲什麼會發生這種情況?我錯過了什麼嗎?我的標準庫沒有足夠優化或者出於某種原因(我不知道),這是完全不安全的嗎?

+4

複製寫入並不是真正的「超級優化」。我相信海灣合作委員會的標準庫仍然使用它,但僅僅是因爲十年前,在多線程是常態之前它纔有意義。今天制定的一個理智的圖書館實施將避免像瘟疫一樣的COW。 – jalf

+2

我認爲該標準甚至不允許COW,因爲成員函數的迭代器失效要求。 – Xeo

+4

Afaik C++ 03允許COW。我相信C++ 11禁止它 – jalf

回答

10

的問題是,有沒有辦法讓std :: string類識別const char*指針是否是一個全球性的文字或不:

const char *a = "Hello World"; 
const char *b = new char[20]; 

的字符*指針可能會在任何時候無效(例如,當它是本地變量並且函數/作用域結束時),因此std::string必須成爲字符串的獨佔所有者。這隻能通過複製來實現。

下面的例子說明爲什麼它是必要的:

std::string getHelloWorld() { 
    char *hello = new char[64]; 
    strcpy(hello, "Hello World"); 
    std::string result = (const char *)hello; // If std::string didn't make a copy, the result could be a garbage 
    delete[] hello; 
    return result; 
} 
+0

實際上,一個字符串文字是一個'char [N]',其中N是長度+ 1(空終止符)。 – Xeo

+2

爲什麼「新」?在堆棧上分配緩衝區也可以:'char const hello [] =「Hello World」;' –

+1

@MatthieuM .:你的版本是異常安全的,而dark_charlie不是字符串構造函數不是no-扔。 –

5

std::string不是銀彈。它的目的是儘可能實現一個通用可變字符串,它擁有自己的內存,並且使用C API非常便宜。這些都是常見的情況,但它們不匹配字符串用法的實例。

正如你所說的,字符串文字不適合這個用例。他們使用靜態分配的內存,因此std::string不能也不應該嘗試獲取內存的所有權。而這些字符串是總是只讀,所以std::string不能讓你修改它們。

std::string創建傳遞給它的字符串數據的副本,然後在內部對此副本進行操作。

如果你想在常量字符串,其壽命是其他地方處理(在字符串的情況下,它是由它初始化並釋放靜態數據的運行時庫處理)進行操作,那麼你可能想使用一個不同的字符串表示。也許只是一個簡單的const char*

+0

*這是擁有它的內存的可變字符串的最佳實現*不。差遠了。 '.c_str()'所要求的嚴格要求成本。當字符串很大並且被修改時,最好的實現可能會使用B-樹來避免所有那些昂貴的重新分配。 –

+0

但它會失去連續性,這將使轉換爲C字符串更昂貴。有很多折衷需要考慮。 :)但我澄清了我的答案了一下。 – jalf

+0

是的,這就是爲什麼我提到'c_str'。我相信SGI STL有一個「繩索」類來覆蓋不需要C交互的情況。 –

21

其實,你的煩惱會消失(*)如果你改變了文字:

std::string const SL = "Hello to all!"; 

我加入了const你。

foo1(SL);   // by const-reference, exact same cost than a pointer 
foo2(SL.c_str()); // simple pointer 

如果你想要移動到std::string,不僅切換功能:現在

,呼籲foo1將不涉及任何複製(所有),並調用foo2可以以很小的成本實現接口,也切換變量(和常量)。 (*)原始答案假定SL是一個全局常量,如果它是一個函數的局部變量,那麼如果真的希望在每次調用時避免構建它,那麼它可以被製作爲static

+0

另外一點:如果字符串文字在某個函數中,則可能需要將其設爲靜態。 –

+4

這並不意味着所有的字符串文字都會在啓動時被複制到堆中嗎? –

+0

爲什麼憂慮會消失?現在,std :: string對象將在輸入/退出範圍內構造/析構,這可能會導致與之前相同的內存分配/釋放(取決於std :: string實現)。使其成爲const不會使其成爲靜態的,是嗎? –