c++ __declspec usage
阿新 • • 發佈:2018-12-25
語法說明:
__declspec ( extended-decl-modifier-seq )
擴充套件修飾符:
1:align(#)
用__declspec(align(#))精確控制使用者自定資料的對齊方式 ,#是對齊值。
e.g
__declspec(align(32))
struct Str1{
int a, b, c, d, e;
};
【轉】它與#pragma pack()是一對兄弟,前者規定了對齊的最小值,後者規定了對齊的最大值。同時出現時,前者優先順序高。 __declspec(align())的一個特點是,
它僅僅規定了資料對齊的位置,而沒有規定資料實際佔用的記憶體長度,當指定的資料被放置在確定的位置之後,其後的資料填充仍然是按照#pragma pack規定的方式填充的,這時候類/結構的實際大小和記憶體格局的規則是這樣的:在__declspec(align())之前,資料按照#pragma pack規定的方式填充,如前所述。當
遇到__declspec(align())的時候,首先尋找距離當前偏移向後最近的對齊點(滿足對齊長度為max(資料自身長度,指定值)),然後把被指定的資料型別從這個
點開始填充,其後的資料型別從它的後面開始,仍然按照#pragma pack填充,直到遇到下一個__declspec(align())。當所有資料填充完畢,把結構的整體對
齊數值和__declspec(align())規定的值做比較,取其中較大的作為整個結構的對齊長度。 特別的,當__declspec(align())指定的數值比對應型別長度小
的時候,這個指定不起作用。
2: allocate("segname")
用__declspec(allocate("segname")) 宣告一個已經分配了資料段的一個數據項。它和#pragma 的code_seg, const_seg, data_seg,section,init_seg配合使用,segname必須有這些東東宣告。
e.g
#pragma data_seg("share_data")
int a = 0;
int b;
#pragma data_seg() __declspec(allocate("share_data")) int c = 1;
__declspec(allocate("share_data")) int d;
3. deprecated
用__declspec(deprecated ) 說明一個函式,型別,或別的識別符號在新的版本或未來版本中不再支援,你不應該用這個函式或型別。它和#pragma deprecated作用一樣。
e.g
#define MY_TEXT "function is deprecated"
void func1(void) {}
__declspec(deprecated) void func1(int) { printf("func1n");}
__declspec(deprecated("** this is a deprecated function **")) void func2(int) { printf("func2n");}
__declspec(deprecated(MY_TEXT)) void func3(int) { printf("func3");}
int main()
{
fun1();
fun2();
fun3();
}
4.dllimport 和dllexport
用__declspec(dllexport),__declspec(dllimport)顯式的定義dll介面給呼叫它的exe或dll檔案,用 dllexport定義的函式不再需要(.def)檔案宣告這些函式介面了。注意:若在dll中定義了模板類那它已經隱式的進行了這兩種宣告,我們只需在 呼叫的時候例項化即可,呵呵。
e.g 常規方式dll中
class ___declspec(dllexport)
testdll{
testdll(){};
~testdll(){};
};
呼叫客戶端中宣告
#import comment(lib, "**.lib)
class ___declspec(dllimportt)
testdll{
testdll(){};
~testdll(){};
};
e.g 模板類:dll中
template<class t>
class test{
test(){};
~test(){};
}
呼叫客戶端中宣告
int main()
{
test< int > b;
return 0;
}
5. jitintrinsic
用__declspec(jitintrinsic)標記一個函式或元素為64位公共語言執行時。具體用法未見到。
6. __declspec( naked )
對於沒有用naked宣告的函式一般編譯器都會產生儲存現場(進入函式時編譯器會產生程式碼來儲存ESI,EDI,EBX,EBP暫存器 ——prolog)和清除現場(退出函式時則產生程式碼恢復這些暫存器的內容——epilog) 程式碼,而對於用naked宣告的函式一般不會產生這些程式碼,這個屬性對於寫裝置驅動程式非常有用,我們自己可以寫這樣一個過程,它僅支援x86 。naked只對函式有效,而對型別定義無效。對於一個標誌了naked的函式不能產生一個行內函數即時使用了__forceinline 關鍵字。
e.g__declspec ( naked ) func()
{
int i;
int j;
__asm /* prolog */
{
push ebp
mov ebp, esp
sub esp, __LOCAL_SIZE
}
/* Function body */
__asm /* epilog */
{
mov esp, ebp
pop ebp
ret
}
}
7. restrict 和 noalias
__declspec(restrict) 和 __declspec(noalias)用於提高程式效能,優化程式。這兩個關鍵字都僅用於函式,restrict針對於函式返回指標,restrict 說明函式返回值沒有被別名化,返回的指標是唯一的,沒有被別的函式指標別名花,也就是說返回指標還沒有被用過是唯一的。編譯器一般會去檢查指標是否可用和 是否被別名化,是否已經在使用,使用了這個關鍵字,編譯器就不在去檢查這些資訊了。noalias 意味著函式呼叫不能修改或引用可見的全域性狀態並且僅僅修改指標引數直接指向的記憶體。如果一個函式指定了noalias關鍵字,優化器認為除引數自生之外, 僅僅引數指標第一級間接是被引用或修改在函式內部。可見全域性狀態是指沒有定義或引用在編碼範圍外的全部資料集,它們的直至不可以取得。編碼範圍是指所有源 檔案或單個原始檔。其實這兩個關鍵字就是給編譯器了一種保證,編譯器信任他就不在進行一些檢查操作了。
e.g
#include <stdio.h>
#include <stdlib.h>
#define M 800#define N 600#define P 700float * mempool, * memptr;
__declspec(restrict) float * ma(int size)
{
float * retval;
retval = memptr;
memptr += size;
return retval;
}
__declspec(restrict) float * init(int m, int n)
{
float * a;
int i, j;
int k=1;
a = ma(m * n);
if (!a) exit(1);
for (i=0; i<m; i++)
for (j=0; j<n; j++)
a[i*n+j] = 0.1/k++;
return a;
}
__declspec(noalias) void multiply(float * a, float * b, float * c)
{
int i, j, k;
for (j=0; j<P; j++)
for (i=0; i<M; i++)
for (k=0; k<N; k++)
c[i * P + j] = a[i * N + k] * b[k * P + j];
}
int main()
{
float * a, * b, * c;
mempool = (float *) malloc(sizeof(float) * (M*N + N*P + M*P));
if (!mempool)
puts("ERROR: Malloc returned null"); exit(1);
memptr = mempool;
a = init(M, N);
b = init(N, P);
c = init(M, P);
multiply(a, b, c);
}
8. noinline__declspec(noinline)
告訴編譯器不去內聯一個具體函式。
9. noreturn__declspec(noreturn)
告訴編譯器沒有返回值.注意新增__declspec(noreturn)到一個不希望返回的函式會導致已沒有定義錯誤.
10.nothrow__declspec(nothrow)
用於函式宣告,它告訴編譯器函式不會丟擲異常。
e.g
#define WINAPI __declspec(nothrow) __stdcall
void WINAPI f1();
void __declspec(nothrow) __stdcall f2();
void __stdcall f3() throw();
11.novtable __declspec(novtable)
用在任意類的宣告,但是隻用在純虛介面類,因此這樣的不能夠被自己例項話.它阻止編譯器初始化虛表指標在構造和析構類的時候,這將移除對關聯到類的虛表的 引用.如果你嘗試這例項化一個有novtable關鍵字的類,它將發生AV(access violation)錯誤.C++裡virtual的缺陷就是vtable會增大程式碼的尺寸,在不需要例項化的類或者純虛介面的時候,用這個關鍵字可以減 小程式碼的大小.
e.g
#if _MSC_VER >= 1100 && !defined(_DEBUG)
#define AFX_NOVTABLE __declspec(novtable)
#else
#define AFX_NOVTABLE
#endif
....
class AFX_NOVTABLE CObject
{
...
};
這是vc裡面的一段程式碼,我們可以看出編譯Release版本時,在CObject前是__declspec(novtable),在debug版本沒有這個限制。
e.g
#include <stdio.h>
struct __declspec(novtable) X
{
virtual void mf();
};
struct Y : public X
{
void mf()
{
printf_s("In Yn");
}
};
12.selectany的作用 (轉)
__declspec(selectany)可以讓我們在.h檔案中初始化一個全域性變數而不是隻能放在.cpp中。比如有一個類,其中有一個靜態變數,那 麼我們可以在.h中通過類似" __declspec(selectany) type class::variable = value; "這樣的程式碼來初始化這個全域性變數。既是該.h被多次include,連結器也會為我們剔除多重定義的錯誤。這個有什麼好處呢,我覺得對於 teamplate的程式設計會有很多便利。
e.g
class test
{
public:
static int t;
};
__declspec(selectany) int test::t = 0;
13.thread
thread 用於宣告一個執行緒本地變數. __declspec(thread)的字首是Microsoft新增給Visual C++編譯器的一個修改符。它告訴編譯器,對應的變數應該放入可執行檔案或DLL檔案中它的自己的節中。__declspec(thread)後面的變數 必須宣告為函式中(或函式外)的一個全域性變數或靜態變數。不能宣告一個型別為__declspec(thread)的區域性變數。
e.g
__declspec(thread)
class X{
public:
int I;
} x; // x is a thread objectX y; // y is not a thread object
14.uuid__declspec(uuid)
用於編譯器關聯一個GUID到一個有uuid屬性的類或結構的宣告或者定義.
e.gstruct __declspec(uuid("00000000-0000-0000-c000-000000000046")) IUnknown;struct __declspec(uuid("{00020400-0000-0000-c000-000000000046}")) IDispatch;我們可以在MFC中檢視原始碼.:)
__declspec ( extended-decl-modifier-seq )
擴充套件修飾符:
1:align(#)
用__declspec(align(#))精確控制使用者自定資料的對齊方式 ,#是對齊值。
e.g
__declspec(align(32))
struct Str1{
int a, b, c, d, e;
};
【轉】它與#pragma pack()是一對兄弟,前者規定了對齊的最小值,後者規定了對齊的最大值。同時出現時,前者優先順序高。 __declspec(align())的一個特點是,
它僅僅規定了資料對齊的位置,而沒有規定資料實際佔用的記憶體長度,當指定的資料被放置在確定的位置之後,其後的資料填充仍然是按照#pragma pack規定的方式填充的,這時候類/結構的實際大小和記憶體格局的規則是這樣的:在__declspec(align())之前,資料按照#pragma pack規定的方式填充,如前所述。當
遇到__declspec(align())的時候,首先尋找距離當前偏移向後最近的對齊點(滿足對齊長度為max(資料自身長度,指定值)),然後把被指定的資料型別從這個
點開始填充,其後的資料型別從它的後面開始,仍然按照#pragma pack填充,直到遇到下一個__declspec(align())。當所有資料填充完畢,把結構的整體對
齊數值和__declspec(align())規定的值做比較,取其中較大的作為整個結構的對齊長度。 特別的,當__declspec(align())指定的數值比對應型別長度小
的時候,這個指定不起作用。
2: allocate("segname")
用__declspec(allocate("segname")) 宣告一個已經分配了資料段的一個數據項。它和#pragma 的code_seg, const_seg, data_seg,section,init_seg配合使用,segname必須有這些東東宣告。
e.g
#pragma data_seg("share_data")
int a = 0;
int b;
#pragma data_seg() __declspec(allocate("share_data")) int c = 1;
__declspec(allocate("share_data")) int d;
3. deprecated
用__declspec(deprecated ) 說明一個函式,型別,或別的識別符號在新的版本或未來版本中不再支援,你不應該用這個函式或型別。它和#pragma deprecated作用一樣。
e.g
#define MY_TEXT "function is deprecated"
void func1(void) {}
__declspec(deprecated) void func1(int) { printf("func1n");}
__declspec(deprecated("** this is a deprecated function **")) void func2(int) { printf("func2n");}
__declspec(deprecated(MY_TEXT)) void func3(int) { printf("func3");}
int main()
{
fun1();
fun2();
fun3();
}
4.dllimport 和dllexport
用__declspec(dllexport),__declspec(dllimport)顯式的定義dll介面給呼叫它的exe或dll檔案,用 dllexport定義的函式不再需要(.def)檔案宣告這些函式介面了。注意:若在dll中定義了模板類那它已經隱式的進行了這兩種宣告,我們只需在 呼叫的時候例項化即可,呵呵。
e.g 常規方式dll中
class ___declspec(dllexport)
testdll{
testdll(){};
~testdll(){};
};
呼叫客戶端中宣告
#import comment(lib, "**.lib)
class ___declspec(dllimportt)
testdll{
testdll(){};
~testdll(){};
};
e.g 模板類:dll中
template<class t>
class test{
test(){};
~test(){};
}
呼叫客戶端中宣告
int main()
{
test< int > b;
return 0;
}
5. jitintrinsic
用__declspec(jitintrinsic)標記一個函式或元素為64位公共語言執行時。具體用法未見到。
6. __declspec( naked )
對於沒有用naked宣告的函式一般編譯器都會產生儲存現場(進入函式時編譯器會產生程式碼來儲存ESI,EDI,EBX,EBP暫存器 ——prolog)和清除現場(退出函式時則產生程式碼恢復這些暫存器的內容——epilog) 程式碼,而對於用naked宣告的函式一般不會產生這些程式碼,這個屬性對於寫裝置驅動程式非常有用,我們自己可以寫這樣一個過程,它僅支援x86 。naked只對函式有效,而對型別定義無效。對於一個標誌了naked的函式不能產生一個行內函數即時使用了__forceinline 關鍵字。
e.g__declspec ( naked ) func()
{
int i;
int j;
__asm /* prolog */
{
push ebp
mov ebp, esp
sub esp, __LOCAL_SIZE
}
/* Function body */
__asm /* epilog */
{
mov esp, ebp
pop ebp
ret
}
}
7. restrict 和 noalias
__declspec(restrict) 和 __declspec(noalias)用於提高程式效能,優化程式。這兩個關鍵字都僅用於函式,restrict針對於函式返回指標,restrict 說明函式返回值沒有被別名化,返回的指標是唯一的,沒有被別的函式指標別名花,也就是說返回指標還沒有被用過是唯一的。編譯器一般會去檢查指標是否可用和 是否被別名化,是否已經在使用,使用了這個關鍵字,編譯器就不在去檢查這些資訊了。noalias 意味著函式呼叫不能修改或引用可見的全域性狀態並且僅僅修改指標引數直接指向的記憶體。如果一個函式指定了noalias關鍵字,優化器認為除引數自生之外, 僅僅引數指標第一級間接是被引用或修改在函式內部。可見全域性狀態是指沒有定義或引用在編碼範圍外的全部資料集,它們的直至不可以取得。編碼範圍是指所有源 檔案或單個原始檔。其實這兩個關鍵字就是給編譯器了一種保證,編譯器信任他就不在進行一些檢查操作了。
e.g
#include <stdio.h>
#include <stdlib.h>
#define M 800#define N 600#define P 700float * mempool, * memptr;
__declspec(restrict) float * ma(int size)
{
float * retval;
retval = memptr;
memptr += size;
return retval;
}
__declspec(restrict) float * init(int m, int n)
{
float * a;
int i, j;
int k=1;
a = ma(m * n);
if (!a) exit(1);
for (i=0; i<m; i++)
for (j=0; j<n; j++)
a[i*n+j] = 0.1/k++;
return a;
}
__declspec(noalias) void multiply(float * a, float * b, float * c)
{
int i, j, k;
for (j=0; j<P; j++)
for (i=0; i<M; i++)
for (k=0; k<N; k++)
c[i * P + j] = a[i * N + k] * b[k * P + j];
}
int main()
{
float * a, * b, * c;
mempool = (float *) malloc(sizeof(float) * (M*N + N*P + M*P));
if (!mempool)
puts("ERROR: Malloc returned null"); exit(1);
memptr = mempool;
a = init(M, N);
b = init(N, P);
c = init(M, P);
multiply(a, b, c);
}
8. noinline__declspec(noinline)
告訴編譯器不去內聯一個具體函式。
9. noreturn__declspec(noreturn)
告訴編譯器沒有返回值.注意新增__declspec(noreturn)到一個不希望返回的函式會導致已沒有定義錯誤.
10.nothrow__declspec(nothrow)
用於函式宣告,它告訴編譯器函式不會丟擲異常。
e.g
#define WINAPI __declspec(nothrow) __stdcall
void WINAPI f1();
void __declspec(nothrow) __stdcall f2();
void __stdcall f3() throw();
11.novtable __declspec(novtable)
用在任意類的宣告,但是隻用在純虛介面類,因此這樣的不能夠被自己例項話.它阻止編譯器初始化虛表指標在構造和析構類的時候,這將移除對關聯到類的虛表的 引用.如果你嘗試這例項化一個有novtable關鍵字的類,它將發生AV(access violation)錯誤.C++裡virtual的缺陷就是vtable會增大程式碼的尺寸,在不需要例項化的類或者純虛介面的時候,用這個關鍵字可以減 小程式碼的大小.
e.g
#if _MSC_VER >= 1100 && !defined(_DEBUG)
#define AFX_NOVTABLE __declspec(novtable)
#else
#define AFX_NOVTABLE
#endif
....
class AFX_NOVTABLE CObject
{
...
};
這是vc裡面的一段程式碼,我們可以看出編譯Release版本時,在CObject前是__declspec(novtable),在debug版本沒有這個限制。
e.g
#include <stdio.h>
struct __declspec(novtable) X
{
virtual void mf();
};
struct Y : public X
{
void mf()
{
printf_s("In Yn");
}
};
12.selectany的作用 (轉)
__declspec(selectany)可以讓我們在.h檔案中初始化一個全域性變數而不是隻能放在.cpp中。比如有一個類,其中有一個靜態變數,那 麼我們可以在.h中通過類似" __declspec(selectany) type class::variable = value; "這樣的程式碼來初始化這個全域性變數。既是該.h被多次include,連結器也會為我們剔除多重定義的錯誤。這個有什麼好處呢,我覺得對於 teamplate的程式設計會有很多便利。
e.g
class test
{
public:
static int t;
};
__declspec(selectany) int test::t = 0;
13.thread
thread 用於宣告一個執行緒本地變數. __declspec(thread)的字首是Microsoft新增給Visual C++編譯器的一個修改符。它告訴編譯器,對應的變數應該放入可執行檔案或DLL檔案中它的自己的節中。__declspec(thread)後面的變數 必須宣告為函式中(或函式外)的一個全域性變數或靜態變數。不能宣告一個型別為__declspec(thread)的區域性變數。
e.g
__declspec(thread)
class X{
public:
int I;
} x; // x is a thread objectX y; // y is not a thread object
14.uuid__declspec(uuid)
用於編譯器關聯一個GUID到一個有uuid屬性的類或結構的宣告或者定義.
e.gstruct __declspec(uuid("00000000-0000-0000-c000-000000000046")) IUnknown;struct __declspec(uuid("{00020400-0000-0000-c000-000000000046}")) IDispatch;我們可以在MFC中檢視原始碼.:)