作者PkmX (阿貓)
看板C_and_CPP
標題Re: [問題] 有關多載及strcmp()的題目
時間Sat Jan 26 23:42:46 2013
分享一下自己看到這題目會怎麼寫
※ 引述《o07608 (無良記者)》之銘言:
怒刪
: 請撰寫一組equal()的多載函式,
看到以上這一句就可以知道題目是要你寫好幾個像這樣的function:
? equal(?)
每一個都叫equal,但是傳入的參數每個會不太相同,
(這就是C++的function overloading的概念)
: 能接受兩個相同型態的引數,若兩者相同則傳回1,否則傳回0。
這裡只有說要傳回0或1,並沒有說型態為何,就先假定是bool吧,所以我們要寫好幾個:
bool equal(?)
: 提供的版本有:char、int、double、char*等型態的引數。
要寫四個equal,加上前面提到"能接受兩個相同型態的引數",推導出要寫的就是:
bool equal(char, char);
bool equal(int, int);
bool equal(double, double);
bool equal(char*, char*);
俗話說:把signatures寫出來,就已經寫完一半了(咦?有這句嗎?)
把function的type寫出來之後,其實要做啥就很清楚了,
如果你拿到兩個char、兩個int或是兩個double,
要如何生出一個bool表示他們是否相同?這你第一天學C就會了,瞬間秒殺:
bool equal(char a, char b) { return a == b; }
bool equal(int a, int b) { return a == b; }
bool equal(double a, double b) { return a == b; } (註1)
: 使用strcmp()函式來測試字串是否相同
char*本身是pointer,你如果直接用==比較大小,其實是比較pointer的值,
也就是字串頭的記憶體位置,而不是真正比較字串的內容,所以要用strcmp,
至於strcmp要怎用?如果不知道或忘了就去查啊XD
http://en.cppreference.com/w/cpp/string/byte/strcmp
這裡他說strcmp吃兩個const char* lhs和rhs,如果lhs和rhs一樣就return 0,
所以我們當然就是把兩個字串傳給strcmp,然後看看結果是不是0,
bool equal(char* a, char* b) { return strcmp(a, b) == 0; }
這樣其實就已經把題目要求的寫完了,剩下就寫些test case看看對不對(註2):
#include <cassert>
int main()
{
assert(equal('a', 'a') == true);
assert(equal('a', 'b') == false);
assert(equal(1, 1) == true);
aseert(equal(1, 0) == false);
assert(equal(1.0, 1.0) == true);
assert(equal(1.0, 2.0) == false);
char* foo1 = new char[4]; // (註3)
strcpy(foo1, "foo");
char* foo2 = new char[4];
strcpy(foo2, "foo");
// Make sure foo1 and foo2 point to different address so that
// equal will fail if it only compares pointer address.
assert(foo1 != foo2 && equal(foo1, foo2) == true);
assert(equal(foo1, "bar") == false);
delete [] foo1;
delete [] foo2;
return 0;
}
至於你問輸入的部份其實和題目就已經沒有相關了,這部份還有很多東西可以探討XD
如果你只是要交作業/應付工作,寫到這邊其實你就可以交差了,
不過如果C++只有這樣我也必要在這邊發廢文了,bjarne也要準備回家吃自己了...
Const-correctness
首先C和C++裡面有一個非常重要的概念是const,它基本上有兩個含意:
1. 如果你宣告一個value為const,代表它的值不會被改變
2. 如果你宣告pointer或reference指向的東西為const,
代表你不會透過這個pointer和reference去修改它(註4)
在參數和變數上適當宣告const可以幫助你避免很多錯誤,
在equal(char*, char*)之中,其實我們只是要透過這個指標看看值一不一樣而已,
並沒有打算要改它,因此我們應該宣告它為equal(const char*, const char*),
除了避免我們不小心在函數內修改到它之外,更重要的是告訴這個function的caller\
你把pointer傳給我,我不會去亂改它指向的東西,這對caller來說很重要,
因為如果caller拿到一個const char*的話,它只能傳給其他接受const char*的函數,
而不能傳給接受char*的函數,否則就變成caller說謊,
它先說說我不會去改它(const char*),然後又偷偷叫別人去改它(用char*傳給別人)
Template
再來可以注意到char、int和double這三個版本其實除了型態以外,其他全部都一樣,
這就是function template可以幫忙的時候了,把一樣的型態抓出來:
template<typename T>
bool equal(T a, T b)
{
return a == b;
}
compiler就會依照你傳入的參數不同,自動幫你生出對應的版本,
無論T是char、int、float、double、long long、const char*或std::string,
反正只要T可以用==來比較就可以了,然後等等...^^^^^^^^^^^這是什麼東西?
前面好像有提到const char*不能直接用==比,這樣生出來的不就不對了嗎?
還好C++有規範:當function template和你寫的function都存在的時候,
會選擇去先去試試看呼叫你特地寫的function,所以:
equal("foo", "bar");
template<typename T>
bool equal(T a, T b)
{
return a == b;
}
bool equal(cosnt char* a, const char* b) // 你自己寫的ordinary function
{
return strcmp(a, b) == 0;
}
雖然把const char*帶入T也可以,但是compiler會選擇你寫的版本,
所以就變成先寫一個最general的版本,再針對特殊case做處理(註5)
當然,以上的template還只是最基本的版本,還有很多延伸的問題可以討論,
例如上面是用call-by-value,如何用call-by-reference避免複製大型物件,
而又要保持primitive type (如int)仍然是pass-by-value,要怎樣寫最general?
(hint:用boost::call_params、std::enable_if...等)
當然如果你初學可以先跳過這些,等熟悉以後再回來看
心血來潮寫了這麼多但是好像沒啥重點(?),就加減看看吧XD
附註
1. float或double直接用==比較會有precision的問題
2. 這就是偽TDD,應該要先寫test case出來XD
3. 不要用new,用std::unique_ptr<char[]>或std::string幫你自動管理記憶體
4. 很多人認為const T*或const T&代表它指向的值不會變,但事實上這觀念是錯的,
它只有說你不會透過這個pointer或reference來改變它而已,
也因為這誤解造就出了無限多的bug和漫罵,詳見:
http://goo.gl/IJPBJ
5. 不是function template specialization,C++是沒有這種東西的,
如果你要特製化function,直接寫一個overloaded function,
在overload resolution的時候就會幫你解決掉
--
※ 發信站: 批踢踢實業坊(ptt.cc)
◆ From: 140.113.235.102
※ 編輯: PkmX 來自: 140.113.235.102 (01/26 23:45)
※ 編輯: PkmX 來自: 140.113.235.102 (01/26 23:45)
→ loveme00835:5. name mangling 還是不同的, 皆可呼叫時還會有優先 01/26 23:51
→ loveme00835: 的考量 01/26 23:51
推 o07608:這怎麼可能只是加減看的等級......Q_Q 01/27 00:03
→ loveme00835:像 P 大一樣有熱情就會學很快了~ 01/27 00:06
推 dendrobium:浮點數根本就不該用==, 不是'比較' 01/27 00:21
→ PkmX:樓上: 我的意思是"用==比較"不是"比較有問題" XD 01/27 00:50
推 EdisonX:equal('a','a') 是呼叫 int 版 ? 01/27 01:09
→ EdisonX: ^嗎 ? 01/27 01:10
→ EdisonX:sorry, 請無視樓上堆文 @@ 01/27 01:13