2012-09-27 69 views
1

我想知道tmp的字符串內存是否可以在下面的代碼中重用。它的內存是否在每次迭代中重新分配?有沒有更好的方法來處理這種情況?C++字符串內存重用優化

string s, line; 
map<string, string> mymap; 
while(getline(file, line) { 
    if(a) s = "_a"; 
    else if(b) s = "_b"; 
    string tmp = line + s; 
    mymap.insert(tmp, s); 
} 
+1

爲什麼不簡單地在while循環之外聲明字符串'tmp'?它將確保在每次迭代中只重複使用一個字符串副本。 –

+2

「我們應該忘記小效率,大約97%的時間:不成熟的優化是所有邪惡的根源」 – rubenvb

+1

rubenvb說的相反。像Knuth這樣的人都很好,他深刻理解他的代碼是如何工作的,而不關心這樣一個點的答案。但是,從一開始就忽略所有性能細節的人都會寫出糟糕的C++,並且不得不花費大量的時間來處理Knuth解決同樣的問題的問題,「忽略性能」,而且開始時速度足夠快。他還表示:「在已建立的工程學科中,容易獲得的12%的改進永遠不會被認爲是邊際的,我相信軟件工程中應該有相同的觀點。」 –

回答

4

tmp每次在循環中創建並銷燬,並在line中獲取字符串數據的副本。所以,你可以得到一個便宜的可能的改進和可任意很難待任,更糟糕的是這樣的:

if(a) s = "_a"; 
else if(b) s = "_b"; 
line += s; 
mymap.insert(line, s); 

我也給sconst char*類型:沒有太多的點分配每一個string一次循環只包含文字的副本。但它通過撥打insert轉換爲string,所以沒有太多的方法。如果你不損害你的代碼的簡單性/可讀性/可維護性/設計來實現它,那麼一個可能改進並且幾乎不可能變得更糟的過程並不是不成熟的優化。 lines的範圍越大,玩弄技巧的風險越大(分別改變值和改變類型),因爲你可能會誤導讀者/維護者。這是短功能好的原因之一。

在C++ 11中,您可以編寫mymap.insert(std::move(line), s);以獲得另一個簡單的可能改進。所有這一切說:你可能會發現,無論你做了多少不必要的複製和分配,與getline中的I/O所用的時間相比,所花費的時間都相形見絀。在這種情況下,編寫代碼有兩種非常類似的方式,其中一種「應該」更有效。所以你可以使用它,但不要過分重視它,認爲它一定會有所作爲。

1

是的,使用在C++ 11 standart中引入的STD :: MOVE語義。

更新: 例如

#include <iostream> 
#include <utility> 
#include <vector> 
#include <string> 
int main() 
{ 
    std::string str = "Hello"; 
    std::vector<std::string> v; 

    // uses the push_back(const T&) overload, which means 
    // we'll incur the cost of copying str 
    v.push_back(str); 
    std::cout << "After copy, str is \"" << str << "\"\n"; 

    // uses the rvalue reference push_back(T&&) overload, 
    // which means no strings will copied; instead, the contents 
    // of str will be moved into the vector. This is less 
    // expensive, but also means str might now be empty. 
    v.push_back(std::move(str)); 
    std::cout << "After move, str is \"" << str << "\"\n"; 

    std::cout << "The contents of the vector are \"" << v[0] 
             << "\", \"" << v[1] << "\"\n"; 
} 
+0

你可以添加一個例子嗎? –

3

while循環的每個迭代將創建和銷燬你的字符串tmp對象。因此,第一步是將tmp移到while循環之外,正如已經提到的那樣。這樣你就不必在每次迭代中構造一個新的字符串對象。但是,您仍然擁有tmp = line + s分配,每次迭代都會導致內存重新分配。使用=運算符創建參數副本,並將副本分配給tmp字符串對象。因此,第二步是添加建議的mymap.insert(line+s, s);,以消除對tmp字符串對象的需求。

我想人們可以繼續這種改進,通過不分配"_a""_b"字符串s每次迭代。這可以在while循環之外完成一次,然後根據ab的內容,可以將不同的字符串對象添加到您的地圖。像這樣的東西(注:這是未經測試):

string a = "_a"; 
string b = "_b"; 
string line; 
map<string, string> mymap; 
while(getline(file, line) { 
    if(a) mymap.insert(line+a, a); 
    else if(b) mymap.insert(line+b, b); 
} 

有人可能會說,如果這是不錯的,在我看來juanchopanza的答案是不夠,因爲它保持代碼的可讀性。但是我認爲上面的代碼有更少的副本。

+3

如果'a'和'b'都不是真的,你可能已經改變了代碼的含義。但是我不確定提問者是否也考慮過這種情況,所以你可能會好起來的:-)順便說一下,在C++中,插入'line + s'直接反轉了你可能從移動' tmp'(因爲它仍然在每個循環中構造一個臨時對象,它只是臨時名稱沒有),所以我認爲你可以聲稱它們都是*改進。在C++ 11中,您應該從實際臨時獲得移動收益,而不是通過'tmp'獲得。 –

+0

不要插入line + a,你應該做line + = a,然後在if/else之後插入一行。 –