2016-11-18 74 views
0

我想知道傳遞自定義字符串的最有效方法是什麼。 例如,我有這樣的代碼段:高效的自定義字符串創建者

outputFile << addSpace(data.len()); 

其中

string addSpace(int n) { 
    string result(""); 
    for (int i = 0; i < n; i++) { 
     result += ' '; 
    } 
    return result; 
} 

清楚的是,該功能不是那麼有效,因爲字符串由-VAL返回,然後,即使在使用RValue可能適合的地方。 如果N是固定的,比如N = 5,我可以只使用

outputFile << " "

但顯然情況並非如此。

那麼最好的解決方案是什麼?(不管這個N空白的具體例子,可以說任何參數相關的字符串創建)。 我想到了lambda函數,但我並不確定。

+2

你大概的意思是「高效」 ... –

+0

如果'data.len'有一個合理的上限,你是經常調用該函數,你可以緩存每個生產串一個函數靜態數組(或關聯數組),然後只用索引長度來檢查,如果存在,則返回緩存的值。或者甚至更鈍,預先產生所有這些字符串,如果max len很小的話。哦,你應該把返回值作爲一個const引用,如果你只是打印它。 –

+0

也許你正在尋找的東西像'的std :: setw',但它不太可能有任何效率差別反正。 –

回答

2

很明顯,功能不那麼有效,因爲返回的字符串由-VAL

的字符串是本地的,所以通過返回值是唯一的選擇。這並不是真正的價值回報,使得函數的效率降低,而是在每次調用中創建一個新字符串。事實上,你必須回報的價值只是該設計的一個症狀。但是,除非你調用該函數很多,具有不同n(如果呼叫者使用同一n,他們可以保持返回的字符串和重用)缺乏效率可能是微不足道的。

你當然可以傳遞一個字符串引用給函數,讓函數修改字符串,然後返回引用,如果該函數被調用很多次,那麼的確可以(可能只是少量)更高效。相同的參數字符串,因爲這樣可以避免每次調用都創建一個新字符串但是,那麼你需要在調用代碼中管理這個外部字符串,這會使該函數的使用更復雜,因此更糟糕。如果你不重複使用參數字符串,那麼這不會更有效。

如果您選擇使用一個新的字符串爲每一個實例,還有一個更簡單(和潛在的更有效的,但更稍微比重新使用字符串差)的方式比你的函數。只需使用的std::string構造:

outputFile << std::string(data.len(), ' '); 

不創建一個字符串都可能會更高效地爲這種特殊情況:

outputFile << std::setfill(' ') << std::setw(data.len()) << ' '; 

所以對於一般的情況下,選擇歸結到:效率比一個漂亮的界面更重要,並且可以重用字符串 - 然後將參考傳遞給字符串並進行修改。否則,返回一個新的字符串,如你的例子更好。

+0

也許通過參考的想法確實是最有效的解決方案。 然而,我不知道是否有一種方法可以將右值傳遞給運算符<<,就像您使用'std :: string'的構造函數的第二種解決方案一樣,但是以一種通用的方式,這意味着我們不會限制自己到我們想重複固定角色的情況。 – GoldenSpecOps

+0

可以創建一個類'customString',實現一個合適的構造函數,並返回字符串的轉換運算符,然後使用它的構造函數,就像您提供的第二個解決方案一樣。我能想到的唯一問題是演員操作會使演出效率低下,但我對此無足夠的瞭解。 – GoldenSpecOps

+0

@anonanon由函數*返回的值是一個右值。 – user2079303

0

您可以使用string.insert函數。函數可以從字符串的給定位置開始插入任意數量的字符。

string & insert(size_t pos,size_t n,char c);

參考:http://www.cplusplus.com/reference/string/string/insert/

例如:

string result = "foo"; 
int n = 5; 
result.insert(result.length(), n, ' '); //result will become "foo  " 
+0

這可能確實是我已經給出了具體的例子非常好,但正如我所說,我正在尋找一個字符串創建多種選項的通用的解決方案,而不只是一些字符的重複。 – GoldenSpecOps

0

我覺得你的函數不是效率低下。讓我們來檢查潛在的低效率:

  • string::operator+=(char)看起來可怕的(並且可能是,自定義類型)。但對於字符串,它不會創建和分配新字符串,它只是將字符附加到現有字符;追加一個字符應該具有恆定的時間複雜度。如果你真的只需要重複字符串中的相同字符,user2079303的構造函數建議確實是創建它的正確方法。

  • 返回結果的值:現代編譯器會移動串出來的功能或構建其就地開始用,而不是創建臨時副本。移動字符串也應該非常快(特別是,沒有動態內存被分配)。

  • 每次調用函數時動態地分配一個字符串這是一個確實效率低下的部分。如果你真的需要字符串(而你的例子只是一個簡單的例子),像user2079303建議的那樣,提交一個對已有字符串的引用是一個很好的解決方法。一些Java Swing API已經被接受預先存在的對象引用的函數修改,以避免短期對象的動態內存操作,就像你的例子。

另一種避免字符串創建的方法是重新設計。如果代碼確實是關於輸出,手一ostream參考功能,讓功能決定如何最有效地輸出東西。正如我所說,這取決於你的用例。

+1

現代(符合C++ 11)編譯器**必須**將字符串移出函數。即使是pre-C++ 11,如果他們實現NRVO,可能會避免複製(並且現代編譯器會避免這種移動)。 – user2079303

+0

@ user2079303感謝您的澄清。 –

+0

哦,我不知道現代編譯器會移出字符串而不是複製它,如果是這種情況,那麼我認爲返回by-val函數的初始設置將會很好。謝謝 – GoldenSpecOps

0

你可以返回一些含有足夠的信息做你的IO操作


在這種情況下,你addspace可以成爲

#include <iostream> 

struct secret_thing_to_add_space{ 
    int number; 
}; 

std::ostream& operator << (std::ostream& os, const secret_thing_to_add_space& s){ 
    //just use simple method here, you can use something different 
    for(int i=0;i<s.number;++i)os<<' '; 
    return os; 
}; 

secret_thing_to_add_space addspace(int n){ 
    return {n}; 
} 

int main(){ 
    std::cout<< "begin" << addspace(10) << "end"; 
} 

正如你所看到的,它變得複雜。 (並且不太可能在這種情況下更有效)