2011-01-09 59 views
3

這是this question的後續行動。假設我編寫了一個接受或返回一個常量字符串的C++接口。我可以用一個const char * 0結尾的字符串:字符串接受界面應該如何?

void f(const char* str); // (1) 

另一種方法是使用一個std :: string的:

void f(const string& str); // (2) 

它也可以編寫過載,同時接受:

void f(const char* str); // (3) 
void f(const string& str); 

或與升壓字符串算法相結合,甚至一個模板:

template<class Range> void f(const Range& str); // (4) 

我的想法是:

  • (1)不是C++雜交,並且可以是效率較低時隨後的操作可能需要知道字符串長度。
  • (2)不好,因爲現在f("long very long C string");調用std :: string的構造,涉及堆分配。如果f使用該字符串只是將其傳遞給一個需要C字符串的低級接口(如fopen),那麼這只是浪費資源。
  • (3)導致代碼重複。儘管一個f可以根據最有效的實現來調用另一個。然而,我們不能根據返回類型來重載,就像std :: exception :: what()那樣返回一個const char *。
  • (4)不適用於單獨的編譯,並可能導致更大的代碼膨脹。
  • 根據實現需要什麼來選擇(1)和(2),好吧,將實現細節泄漏到接口。

問題是:什麼是優先的方式?是否有任何單一的指導方針可以遵循?你有什麼經驗?

編輯:還有一個第五個選項:

void f(boost::iterator_range<const char*> str); // (5) 

其具有(1)(不需要構造一個字符串對象)和(2)(所述的尺寸的優點字符串顯式傳遞給函數)。

+0

)。字符串將在堆棧上構建 – 2011-01-09 17:58:52

+0

@nice:正確的,std :: string本身被分配到堆棧上。但是如果你的字符串足夠長或者你的實現沒有使用短字符串優化,那麼std :: string將在堆上分配它的存儲空間。 – ybungalobill 2011-01-09 18:02:12

回答

1

用於拍攝參數我會用什麼是最簡單的,往往是const char*去。這與零成本的字符串文字一起工作,並且從存儲在std:string中的東西檢索const char*通常也是非常低的成本。

就個人而言,我不會過載打擾。除了最簡單的情況外,您都希望合併到兩個代碼路徑中,並且在某個時刻有一個調用另一個代碼路徑,或者都調用一個常用函數。有人可能會認爲,過載會隱藏一個人是否轉換爲另一個,哪個路徑的成本較高。

只有當我真正想用函數內部的std::string接口const功能我會在界面本身const std::string&,我不知道,只是用size()就足夠的理由的。

在許多項目中,是好還是壞,替代字符串類經常被使用。其中很多,如std::string可以便宜地訪問零終止的const char*;轉換爲std::string需要一份副本。在接口中需要const std::string&即使在函數的內部不需要指定時也會規定存儲策略。我認爲這是不可取的,就像採取const shared_ptr<X>&指示存儲策略,而採用X&(如果可能),允許調用者爲傳遞的對象使用任何存儲策略。

const char*一的缺點是,單純從界面的角度來看,它不執行非NULL的含量(儘管很偶然betweem null參數和一個空字符串的差在一些接口使用 - 這不能可以用std::string來完成),而const char*可能只是單個字符的地址。然而,在實踐中,使用const char*來傳遞一個字符串非常普遍,所以我認爲這是一個相當微不足道的擔憂。其他問題,例如接口文檔中指定的字符編碼(適用於std::stringconst char*)是否更重要,可能會導致更多工作。

7

如果你正在處理一個純粹的C++代碼庫,那麼我會去#2,而不用擔心函數的調用者不會在std :: string中使用它,直到出現問題。與往常一樣,除非出現問題,否則不要過多擔心優化。讓你的代碼乾淨,易於閱讀,並且易於擴展。

+0

但是,爲什麼你比較喜歡2 1?它不會更乾淨,更容易閱讀或更容易擴展! – ybungalobill 2011-01-09 17:48:48

+1

@ybungalobill:因爲如果我正在編寫C++,我寧願處理C++構造,除非遇到需要開始處理的性能問題。 – 2011-01-09 17:49:47

+3

@ybungalobill`const char * str`是根據定義指向char *的*指針。這是一個*字符串*只是按照慣例。這就是爲什麼在C++中,2更乾淨。 – Oswald 2011-01-09 17:54:23

4

您可以遵循一條指導原則:使用(2)除非您有很好的理由不要。

A const char* str作爲參數沒有明確說明,允許在str上執行哪些操作。在它發生段錯誤之前,它多久可以增加一次?它是指向一個char,一個char的數組還是一個C字符串(即一個零終止數組char)?

0

它也可以寫一個 超載,同時接受:

void f(const string& str)已經接受這既是因爲從const char*std::string的隱式轉換。所以#3比#2幾乎沒有什麼優勢。

+1

它避免了轉換。它允許實現決定哪個版本更好。 – ybungalobill 2011-01-09 17:53:11

0

答案應該在很大程度上依賴於您打算在f做什麼。如果你需要對字符串進行一些複雜的處理,那麼方法2是有意義的,如果你只需要傳遞給其他函數,那麼就根據其他函數進行選擇(比方說,爲了參數,你打開一個文件 - 會怎樣最有意義的;))

+0

這泄漏了一個實現細節。這正是我想要避免的。 – ybungalobill 2011-01-09 17:57:03

0

我會選擇void f(const string& str)如果函數體沒有做char -analysis;意味着它不是指strchar*

0

使用(2)。

首先指出問題,它不是一個問題,因爲字符串有在某些時候要創建不管。

煩惱於第二點氣味過早的優化。除非你有堆分配有問題的特定情況,例如重複的字符串文字調用,而且這些情況不能改變,那麼最好避免使用這種缺陷。然後纔可以考慮選項(3)。 (2)明確傳達該功能所接受的內容,並且具有正確的限制。

當然,所有5個都比foo(char*)有所改進,我遇到的不止是我會提及的。

3

我真的沒有一個硬性偏好。根據具體情況,我會在大部分示例中進行交替。

我有時用另一種方法是類似於您Range例子,但使用普通的舊迭代器範圍:

template <typename Iter> 
void f(Iter first, Iter last); 

它有它用C風格的字符串工作輕鬆(和允許被叫很好的特性以恆定時間確定字符串的長度)以及std::string

如果模板是有問題的(也許是因爲我不想在頭要定義的函數),我有時會做同樣的,但使用char*迭代器:

void f(const char* first, const char* last); 

同樣,也可以是(我記得,C++ 03沒有明確要求字符串是連續的,但我知道的每個實現都使用連續分配的字符串,並且我相信C++ 0x將明確要求它)。

因此,除了支持兩種主要字符串類型之外,這些版本都允許傳遞比普通C風格const char*參數(丟失關於字符串長度的信息,並且不處理嵌入式空值)更多的信息(也可能是其他任何你能想到的字符串類)。

不利的一面是你最終得到一個額外的參數。

不幸的是,字符串處理並不是真正的C++最強大的一面,所以我不認爲有一個「最好」的方法。但迭代器對是我傾向於使用的幾種方法之一。 (2)在沒有堆分配的情況下(