目前還有相當一部分開發人員在使用老式編譯器幹活,這些老式編譯器可能對C++98支援不夠。因此,當你的代碼移植到這些老式的編譯器上時,可能會碰到一些稀奇古怪的問題(包括編譯出錯和執行階段錯誤)。下面這些注意事項有助於你繞過這些問題。
強調一下,後面提到的好幾個條款都是通過迴避C++的新文法來保證移植性。如果你用的是新式編譯器,那麼你可以不理會這些條款。
★小心for迴圈變數的範圍(不支援新標準)
在C++98標準中,for迴圈變數的範圍局限在迴圈體內。而某些老的編譯器(例如Visual C++ 6)認為for迴圈變數的範圍在迴圈體外。所以如下的代碼可能導致移植問題。
{
for(int i=0; i<XX; i++)
{
// ...
}
for(int i=0; i<XXX; i++)
{
// ...
}
}
建議修改為不同的迴圈變數,如下所示:
{
for(int i=0; i<XX; i++)
{
// ...
}
for(int j=0; j<XXX; j++)
{
// ...
}
}
★不要使用全域類對象,改用單鍵(標準未定義)
全域類對象的建構函式先於main()函數執行,如果某個模組中同時包含若干個全域類對象,則它們的建構函式的調用順序是不確定的。而單鍵是在第一次調用時被初始化,能避免此問題。另外,單鍵雖然解決了構造問題,但是析構依然有隱患。詳見“C++ 對象是怎麼死的?進程篇”。
★保持inline函數盡量簡單
不要在inline函數內部使用局部靜態變數,不要在inline函數使用可變參數。這些都有可能導致移植問題。
★不要依賴函數參數的求值順序(標準未定義)
標準沒有明確規定函數參數的求值順序。因此,如下的程式碼為是不確定的。
void Foo(int a, int b);
int n = 1;
foo(++n, ++n);
★慎用模板特化(不支援新標準)
有些老式編譯器對偏特化或全特化支援不夠。
★模板繼承中,引用基類成員要小心(不支援新標準)
看如下例子:
template <typename T>
class TBase
{
protected:
typedef std::vector<T> Container;
Container m_container;
};
template <typename T>
class TDerived : public TBase<T>
{
typedef TBase<T> BaseClass;
public:
void Func()
{
typename BaseClass::Container foo; //可移植
Container foo; //不可移植
this->m_container.clear(); //可移植
m_container.clear(); //不可移植
}
};
★慎用RTTI(不支援新標準、標準未定義)
先聲明一下,我這裡說的RTTI主要是指typeid操作符和type_info類型。
首先,由於某些老式編譯器可能不支援typeid操作符和type_info類型,會導致移植性的問題,這是慎用RTTI的一個原因。(如果你用的是新式編譯器,不用考慮這個因素)
其次,由於標準對於type_info類型的約束比較簡單。這導致了不同的編譯器對type_info的實現有較大差異。如果你確實要使用type_info類型,建議僅僅使用它的operator==和operator!=這兩個成員函數。
所以,如果你確實需要在運行時確定類型,又不想碰到上述問題,可以考慮在自己的類體系中加入類型資訊來實現。比如MFC和wxWidgets都是這麼乾的。
★慎用嵌套類(不支援新標準)
如果在內部類訪問外部類的非公有成員,要把內部類聲明為外部類的friend。
如下代碼存在移植問題。
class COuter
{
private:
char* m_name;
public:
class CInner
{
void Print(COuter* outer)
{
cout << outer->m_name;
}
};
};
應該改為如下代碼
class COuter
{
private:
char* m_name;
public:
class CInner; //前置聲明
friend class CInner;
class CInner
{
void Print(COuter* outer)
{
cout << outer->m_name;
}
};
};
★不要定義參數類型相近的函數(標準未定義)
void Foo(short n);
void Foo(long n);
Foo(0); //會導致二義性錯誤
★不要依賴標準類型的字長(標準未定義)
某些標準類型(例如int、wchar_t)的字長會隨著具體的平台而改變。
★用枚舉代替類的靜態成員常量(不支援新標準)
某些老式的編譯器不支援類的靜態成員常量,可以用枚舉來代替。
class CFoo
{
static const int MIN = 0; //不可移植
enum { MAX = 64 }; //可移植
};
今天說了這麼一大堆,都比較瑣碎,估計會有遺漏的。日後如果大伙兒發現有補充的,歡迎在本帖的評論中指教一二。由於篇幅有限,我把和異常相關的內容留到下一個話題。
http://program-think.blogspot.com/2009/01/cxx-cross-platform-develop-2-language.html