Q: 'this'指標是什麼?
A: 一般人很容易誤以為'this'指標是一個class或struct的隱函成員。實際上它是non-static member function的隱藏參數。當你宣告一個function時,Compiler會自動在前面加上這個參數到你function的prototype中。這個參數的形別是根據你在如何宣告你的function。根據C++標準,9.3.2.1中所提到:
在nonstatic member function中,關鍵字'this'是一個non-lvalue expression,它的值是目前呼叫此function的物件之記憶體位置。若此function是宣告在class X中,則這個'this'的形別就是X*。若這個member function是被宣告為const,則此'this'的形別就是const X*,如果這個function宣告為volatile,則這個'this'的形別就是volatile X*,如果這個function被宣告為const volatile,則此'this'的形別就是const volatile X*。
比如說:
class T
{
public:
void foo(int a);
int goo() const;
};
實際上是:
class T
{
public:
void foo(T* this , int a);
int goo(const T* this) const;
};
Static member function不會有這個額外的參數。你不能使用non-static member function做為thread function,即使它有正確的prototype。(One consequence is that you cannot use a non-static member function as a thread function even if it has the correct prototype,不太確定對不對)
比如:
UINT ThreadFunction(LPVOID param);
因為上述的理由,會變成:
UINT ThreadFunction(T* this, LPVOID param);
Q: 如何把數值形別變成字串形別?
A:舊時 C 的做法 (已不被建議使用):char c[10]; // simply large enough - don't forget the
// extra byte needed for the trailing '\0'
int i = 1234;
sprintf(c, "%d", i);
可以在如MSDN查到'sprintf()'的細節。
使用 'CString':
int i = 1234;
CString cs;
cs.Format("%d", i);
使用的方式類似於'sprintf()'。可以在MSDN查到更多細節。
注意: 如果指定的specifiers (也就是上面'%d'的部份)與實際傳入的參數不同,將會造成不可預期的結果,'sprintf()'與'CString::Format()'都要小心這種錯誤。
C++的做法
下面的這個例子告訴你如何使用標準的C++ class來做:
#include <string>
#include <sstream>
#include <iostream>
template <class T>
std::string to_string(T t, std::ios_base & (*f)(std::ios_base&))
{
std::ostringstream oss;
oss << f << t;
return oss.str();
}
int main()
{
// the second parameter of to_string() should be one of
// std::hex, std::dec or std::oct
std::cout<<to_string<long>(123456, std::hex)<<std::endl;
std::cout<<to_string<long>(123456, std::oct)<<std::endl;
return 0;
}
/* output:
1e240
361100
*/
這個方法不只非常漂亮,而且是type safe的,因為Compiler在compile time時根據運算元(operand)的形別選擇適當的'std::ostringstream::operator << ()'。
另外: (下面這段不知道怎麼翻)
There is a new method with 'boost::format', which combines the advantages of printf with the type-safety and extensibility of streams.
One of the advantages (as with printf) is you can store the entire format string as a template (not a C++ template). This is better for internationalisation (making string tables), as well as the fact that even just using a local string table can significantly reduce the code-size.
Q: 標準之前(pre-standard)的傳統(traditional) C++ header是怎麼回事?
A: 傳統的C++ header定義classes, values, macros, 以及function,並且以.h做為副檔名(extension)。包括了非標準的STL header,也是以.h做為副檔名 (iostream.h, fstream.h, vector.h, ... 諸如此類),以及過去C的header也是 (stdlib.h, stdio.h, ... 諸如此類)。標準之前的header具有全域的 namespace (Pre-standard headers have all the code in the global namespace,不太確定怎麼翻這句。)
Q: C++的標準header是怎麼回事?
A: 就是那些已經被列入了C++標準的headers。
Q: 我怎麼知道某個header是不是C++的標準header?
A: 命名的方式和傳統的header命名是一樣的,只是去掉了'.h'的副檔名。比如說,'fstream.h'變成了'fstream','memory.h'變成了'memory',諸如此類。
Q: 'iostream.h'是怎麼回事?
A: 這個'iostream.h'從未成為官方的C++標準,這代表任何實作iostream.h的C++ compiler,可以依著它想要的方式去做,而這樣的compiler仍舊會被視為與C++標準相容的。無論如何,C++標準有把input/output library列入其中。
Q: 傳統ANSI C的標準header是怎麼回事?
A: 傳統的ANSI C標準header是以'c'字母為開頭。因此,如'stdio.h'就變成了'cstdio','stdlib.h'就變成了'cstdlib','math.h'就變成了'cmath','time.h'就變成了'ctime',依此類推。以下是完整的列表:
- <assert.h> - <cassert>
- <ctype.h> - <cctype>
- <errno.h> - <cerrno>
- <float.h> - <cfloat>
- <iso646.h> - <ciso646>
- <limits.h> - <climits>
- <locale.h> - <clocale>
- <math.h> - <cmath>
- <setjmp.h> - <csetjmp>
- <signal.h> - <csignal>
- <stdarg.h> - <cstdarg>
- <stddef.h> - <cstddef>
- <stdio.h> - <cstdio>
- <stdlib.h> - <cstdlib>
- <string.h> - <cstring>
- <time.h> - <ctime>
- <wchar.h> - <cwchar>
- <wtype.h> - <cwtype>
Q: 哪一些是C++標準header?
A: 以下是列表:
語言支援:
- Types: <cstddef>
- Implementation properties: <limits>, <climits>, <cfloat>
- Start and termination: <cstdlib>
動態記憶體管理: <new>
- Type identification: <typeinfo>
- Exception handling: <exception>
- Other runtime support: <cstdarg>, <csetjmp>, <ctime>, <csignal>, <cstdlib>
偵錯:
- Exception classes: <stdexcept>
- Assertions: <cassert>
- Error numbers: <cerrno>
一般公用:
- Utility components: <utility>
- Function objects: <functional>
- Memory: <memory>
- Date and time: <ctime>
字串:
- Character traits: <string>
- String classes: <string>
- Null-terminated sequence utilities: <cctype>, <cwctype>, <cstring>, <cwchar>, <cstdlib>
本地化:
- Locales: <locale>
- C library locales: <clocale>
容器:
- Sequences: <deque>, <list>, <queue>, <stack>, <vector>
- Associative containers: <map>, <set>
- Bitset: <bitset>
- Iterators: <iterator>
演算法:
- Non-modifying sequence operations: <algorithm>
- C library algorithms: <cstdlib>
數值:
- Complex numbers: <complex>
- Numeric arrays: <valarray>
- Generalized numeric operations: <numeric>
- C library: <cmath>, <cstdlib>
輸入/輸出:
- Forward declarations: <iosfwd>
- Standard iostream objects: <iostream>
- Iostreams base classes: <ios>
- Stream buffers: <streambuf>
- Formatting and manipulators: <istream>, <ostream>, <iomanip>
- String streams: <sstream>, <cstdlib>
- File streams: <fstream>, <cstdio>, <cwchar>
Q: 我仍舊可以使用舊版的C header嗎 (stdlib.h, stdio.h, etc.)?
A: 所有與C++相容的compiler都支援上述那18個以'.h'做為結尾的header檔,即使它們是不被建議使用的 (stdlib.h, stdio.h, etc.)。這些*.h的 C header檔都是global namespace。使用這些版本的C header仍舊被視為具有可攜性的 (portability),但它有可能與未來的標準不相容。無論如何,這些header在未來不太完全可能被官方標準移除。
Q: 我仍舊可以使用那些非標準的,以'.h'做為副檔名版本的STL header嗎 (iostream.h, fstream.h, vector.h, etc.) ?
A: 這樣的STL header從未成為C++官方標準,大部份的C++ Compiler支援這樣非標準的header,也有些過時的Compiler(如Turbo C++)只支援這樣非標準的寫法。有些更新穎的Compiler,如VC++ 7.1已經不再支援這樣非標準的寫法。除非你必須要使用這些過時的Compiler,否則的話最好是使用標準的寫法 (<iostream>, <fstream>, <vector>
, etc.)。使用*.h的寫法不好。
另外:
在某些Compiler上,你可以發現它同時有'iostream'以及'iostream.h'。如上所述,在include 'iostream'時那些成員(members)是在std namespace,而'iostream.h'是在global namespace。這可能會在某些時後造成混淆(ambiguities),namespace的設計就是為了避免這樣的情況。這也是建議不要使用*.h的原因之一。
Q: 到底ntohl()與htonl()做了什麼?
A: 'ntohl()'與'htonl()'是四個相關function中的其中二個,另外二個是'ntohs()'與'htons()',以下是Linux manual中的解釋:
- 'htonl()' function將unsigned integer hostlong從host byte order轉為network byte order.
- 'htons()' function將unsigned short integer hostshort從host byte order轉為network byte order.
- 'htohl()' function將unsigned integer netlong從network byte order轉為host byte order.
- 'ntohs()' function將unsigned short integer netshort從network byte order轉為host byte order.
這四個function在host byte order與network byte order之間做轉換。當二者的byte order不同時,則使用這些function會造成endian-ness的改變。當二者的byte ordre一樣時,則不會有任何的改變。因此,當你只是單純想要做endian-ness的轉換(無關乎平台),則不該使用這些function。
這一篇FAQ告訴你如何做無關乎平台的endian-ness轉換。
Special notes:- 'ntohs()', 'ntohl()', 'htons()', and 'htonl()' 並非C的標準函式,所以不能保證portability。
- POSIX 實作'ntohs()', 'ntohl()', 'htons()' and 'htonl()' 時,參數是以'uint16_t' 與 'uint32_t'做為type,這二個type可以在netinet/in.h這二個header file中找到。
- Windows實作使用'unsigned short'與'unsigned long',可以在winsock2.h這個header file中找到。
- 'ntoht()' 與 'htont()' 在其它的系統上可能還有著不同的變形,像是'ntohi()'/'htoni()' 或 'ntohll()'/'htonll()'.
Q: 我如何轉換Big-Endian與Little-Endian的格式?
A:
轉換的動作是可逆的 (Big-Endian <-> Little-Endian),下面這個例子可以用來轉換unsigned data types.
inline void endian_swap(unsigned short& x)
{
x = (x>>8) |
(x<<8);
}
inline void endian_swap(unsigned int& x)
{
x = (x>>24) |
((x<<8) & 0x00FF0000) |
((x>>8) & 0x0000FF00) |
(x<<24);
}
// __int64 for MSVC, "long long" for gcc
inline void endian_swap(unsigned __int64& x)
{
x = (x>>56) |
((x<<40) & 0x00FF000000000000) |
((x<<24) & 0x0000FF0000000000) |
((x<<8) & 0x000000FF00000000) |
((x>>8) & 0x00000000FF000000) |
((x>>24) & 0x0000000000FF0000) |
((x>>40) & 0x000000000000FF00) |
(x<<56);
}
Q: 為何不直接使用ntohl()與htonl()就好了?
A: 這二個function:
- 不一定會改變endian-ness
- 不一定具有可攜性 (portability)
更多的資訊,請看
到底ntohl()與htonl()做了什麼?
The purpose I made this Blog is that when I see the
C++ FAQ in CodeGuru, I think it is very useful for any newbie who is studying C++, so I want to translate it into Chinese. I am a C++ newbie too, I will try to do my best to translate the FAQ, and any comment is appreciated.