#1.模板類的模板成員函數
template <typename type1>
class C {
public:
template <typename type2>
void f();
}
如果需要在類外定義函數f時,要這樣寫:
template<typename type1, typename type2>
void C<type1>::f() {...}
而不能寫成
template<typename type1>
template<typename type2>
void C<type1>::f() {...}
#2.宏定義中的“#”,“##”,“#@”用法
“#”用於將宏參數轉化爲字符串,即在宏參數的兩邊加上雙引號。例如:
#define STR(x) #x
int abc = 1;
cout << STR(abc) << endl;
上面會輸出程序會將會輸出字符串“abc”而不是變量abc的值。
“##”用於宏參數之間的連接或宏變量與其他代碼的連接。例如:
#define VAL1(x) x##1
#define VAL2(x, y) x##y
int abc1 = 1;
cout << VAL1(x) << endl;
cout << VAL2(x, 1) << endl;
兩個輸出語句都會輸出變量abc1的值。
“#@”是將宏參數轉成字符,即在宏參數的兩邊加上單引號,不過gcc不支持。
在使用“#”,“##”,“#@”時,如果宏參數本身也是宏的話,該宏參數是不會進行宏展開的,這裏用一個簡單的例子解釋下:
#define STR(x) #x
cout << STR(__LINE__) << endl;
上面的代碼並不會將宏__LINE__展開,因此得到的輸出是“__LINE__”,而不是當前代碼行號的字符串表示。爲了能夠將__LINE__展開,我們需要藉助轉換宏。
#define _STR(x) #x
#define STR(x) _STR(x)
cout << STR(__LINE__) << endl;
這樣宏__LINE__就會在調用STR的時候展開,因此傳給_STR的就是__LINE__的值了。#3. 匿名命名空間
匿名空間中定義的函數或變量的作用域被限制在文件範圍內,功能等同於用static修飾這些函數或變量。例如:
#a.cpp
void f1(){...};
static void f2(){...};
namespace {
void f3(){...};
}
#b.cpp
extern void f1(); //ok
extern void f2(); //鏈接出錯
extern void f3(); //鏈接出錯
當父命名空間中定義了一個嵌套的普通命名空間時,訪問該嵌套命名空間中函數或變量等時需要使用域作用符。但當該嵌套命名空間換成一個內聯命名空間時,該內聯命令空間中的函數或變量等自動成爲父命名空間的成員,訪問時不需要域作用符。版本控制場景下可能會需要這個特性,下面舉個例子說明:
namespace Pro {
namespace v1 {
void f1();
void f2();
}
inline namespace v2 {
void f1();
}
}
當你的項目版本1提供兩個函數f1和f2,但版本2修改f1並且去掉了f2,這時你如果調用Pro::f1那麼調用的函數是內聯命名空間v2中的f1,並且調用Pro::f2會報錯,如果你確實需要用到老版本中的函數,你可以這樣調用:Pro::v1::f1和Pro::v1::f2。
#5.對齊
編譯器中提供了#pragma pack(n)來設定變量以n字節對齊方式。n字節對齊就是說變量存放的起始地址的偏移量有兩種情況:第一、如果n大於等於該變量所佔用的字節數,那麼偏移量必須滿足默認的對齊方式,第二、如果n小於該變量的類型所佔用的字節數,那麼偏移量爲n的倍數,不用滿足默認的對齊方式。結構的總大小也有個約束條件,分下面兩種情況:如果n大於所有成員變量類型所佔用的字節數,那麼結構的總大小必須爲佔用空間最大的變量佔用的空間數的倍數;否則必須爲n的倍數。