2008-12-04 58 views
7

我已經習慣了周圍的字符串傳遞這樣在我的C++應用程序:C++字符串,可以爲空

void foo(const std::string& input) 
{ 
    std::cout << input.size() << std::endl; 
} 

void bar() 
{ 
    foo("stackoverflow"); 
} 

現在我有我想要的字符串爲NULL的情況:

void baz() 
{ 
    foo("stackoverflow"); 
    foo(NULL); // very bad with foo implementation above 
} 

我可以改變foo到:

void foo(const std::string* input) 
{ 
    // TODO: support NULL input 
    std::cout << input->size() << std::endl; 
} 

但傳遞一個字符串文字或複製char*foo,落實我需要寫是這樣的:

void bar() 
{ 
    string input("hi"); // annoying temporary 
    foo(&input); 
    foo(NULL); // will work as long as foo handles NULL properly 
} 

我開始思考從std::string繼承並添加null屬性,但我不太相信這是一個好主意。也許最好簡單地將const char*字符串用於可以爲NULL的參數,但如果我想保存字符串(或NULL)的副本而不必自己管理其內存,該怎麼辦? (請參閱What are some of the drawbacks to using C-style strings?等)

任何巧妙的解決方案?

+0

爲什麼你想要一個NULL?足夠了? – Tim 2008-12-04 19:33:46

+0

@Tim:也許吧。它會使我無需始終檢查這些參數是否爲NULL,並使用empty()來檢查「無值」。 – 2008-12-04 19:44:24

+0

重新引入NULL是假的。如果你需要可怕的NULL指針,爲什麼你首先使用引用?實際上使用引用的一個最重要的原因是你可以確定它不是NULL。 – Thorsten79 2008-12-04 22:03:26

回答

20

如果你想要的類型爲空,然後使它的指針。傳遞字符串指針而不是引用,因爲這正是指針可以執行的操作,並且引用不能。引用始終指向相同的有效對象。指針可以設置爲null,或者重新指向指向另一個對象。因此,如果你需要指針可以做的事情,請使用指針。

或者,使用boost :: optional,它允許使用更安全的方式來指定「此變量可能包含或不包含值」。

或者,當然,更改語義,以便您使用空字符串而不是null,傳遞一個單獨的布爾參數指定字符串是否可用,或重構,所以你不需要這第一個地方。

1

如果你只是用:

void foo(const char *xinput) 
{ 
    if (xinput == NULL) { 
     // do something exceptional with this 
     return; 
    } 
    std::string input(xinput); 
    // remainder of code as usual 
} 

是的,這並不產生額外的分配和複製,並調用該函數是一個比較詳細的,因爲你需要在通常情況下使用.c_str(),但它確實會給你你想要的語義。

11

就個人而言,我會改變的語義來傳遞,而不是NULL周圍空的std ::字符串:

void foo(const std::string& input) 
{ 
    if (!input.empty()) 
     std::cout << input.size() << std::endl; 
} 

void bar() 
{ 
     foo(""); 
} 
+1

兩點:第一,空字符串可以是有效值,與空分開。其次,當您關心的是大小是否爲非零時,使用`empty()`而不是`size()`被認爲是一個更好的主意。 `boost :: optional`或指針是更好的解決方案。 – 2008-12-04 20:51:09

2

爲什麼不重載函數並給第二個重載沒有參數?然後兩個重載都可以在內部調用一個提供讀取邏輯的函數,並且它本身通過一個指向std::string的指針。

void foo_impl(string const* pstr) { … } 

void foo(string const& str) { 
    foo_impl(&str); 
} 

void foo() { 
    foo_impl(0); 
} 
11

函數重載救援...

void foo(const std::string& input) 
{ 
    std::cout << input << std::endl; 

    // do more things ... 
} 

void foo(const char* input) 
{ 
    if (input != NULL) foo(std::string(input)); 
} 

這將同時接受C風格字符數組和std ::字符串,並會招致在堆棧上額外的開銷,如果你在一傳字符串文字或字符數組,但允許您將實現保留在一個地方並保持良好的語法。

3

,或者在混合了一下前兩個答案:

void fooImpl(const char* input) 
{ 
    if (input != NULL) 
     std::cout << input << std::endl; 
} 

void foo(const std::string& input) 
{ 
    fooImpl(input.c_str());  
} 

void foo(const char* input) 
{ 
    fooImpl(input); 
} 

相同的接口,在堆棧上沒有副本。你可以,如果你喜歡,也可以內聯fooImpl。

2

絕對不會從std::string繼承。繼承是C++中最緊密的耦合,而且你只能尋找可空性,如果你真的想,你可以簡單地使用const char*,重載或簡單地std::string *