概述

__declspec 是 Microsoft Visual C++ 特有的编译器扩展属性,用于向编译器/链接器传递额外的修饰信息。
语法:__declspec(attribute) 或 __declspec(attribute("message"))

[TOC]

基本语法

C++cpp
__declspec(属性名)
__declspec(属性名("可选字符串参数"))

放置位置:紧跟类型声明或函数声明之前(或之后,取决于属性类型)。

C++cpp
// 函数前
__declspec(noinline) void Foo();
 
// 类声明前
class __declspec(dllexport) MyClass { ... };
 
// 变量前
__declspec(selectany) int g_Value = 0;

常用属性

deprecated — 标记已弃用

标记函数/变量已过时,调用方编译时触发 C4996 警告,并显示自定义提示信息。

C++cpp
__declspec(deprecated("Get360SafeProductType已经被AutoCheckProductType取代,推荐优先使用AutoCheckProductType"))
int Get360SafeProductType();

编译时输出:

warning C4996: 'Get360SafeProductType': Get360SafeProductType已经被AutoCheckProductType取代...

使用场景: API 演进、接口迁移、平滑下线旧函数,给调用方充足的缓冲期。

跨平台等价写法

  • C++14 标准属性(推荐):[[deprecated("message")]]
  • GCC/Clang:__attribute__((deprecated("message")))

dllexport / dllimport — DLL 导入导出

控制符号是否从 DLL 导出,或从外部 DLL 导入。

C++cpp
// 导出(在 DLL 项目中使用)
__declspec(dllexport) int MyFunc();
__declspec(dllexport) class MyClass { ... };
 
// 导入(在调用方项目中使用)
__declspec(dllimport) int MyFunc();

惯用宏模式(推荐): 通过预处理宏在导出/导入间自动切换

C++cpp
// MyLib.h
#ifdef MYLIB_EXPORTS
    #define MYLIB_API __declspec(dllexport)
#else
    #define MYLIB_API __declspec(dllimport)
#endif
 
MYLIB_API int MyFunc();
MYLIB_API class MyClass { ... };

在 DLL 项目预处理器定义中添加 MYLIB_EXPORTS,调用方不定义此宏即可自动切换为 dllimport。


selectany — 允许多重定义

告知链接器:若同一符号出现在多个编译单元中,任取其一,其余丢弃(与 COMDAT 机制配合)。

C++cpp
// MyHeader.h
__declspec(selectany) int g_GlobalCounter = 0;

被多个 .cpp 文件 #include 时不会产生重定义链接错误。

MSDN 原文说明

Tells the compiler that the declared global data item (variable or object) is a pick-any COMDAT (a packaged function). At link time, if multiple definitions of COMDAT are seen, the linker picks one and discards the rest. Selectany can be used in initializing global data defined by headers, when the same header appears in more than one source file.

直白翻译: 当在头文件定义全局变量,并且这个头文件被 include 多次时,可以用这个开关剔除由于多次 include 而产生的重定义错误。链接器会从多个编译单元中任选一个定义,其余丢弃。

典型场景: 在头文件中定义初始化的全局变量,避免手动在某个 .cpp 中单独定义。


noinline — 禁止内联

强制编译器不将该函数展开为内联,保留独立的函数调用。

C++cpp
__declspec(noinline) void CriticalSection() {
    // 调试、性能分析时需要保留调用栈帧
}

使用场景:

  • 需要准确的调用栈(崩溃分析)
  • 函数体较大,内联反而增大代码体积
  • 与 __declspec(dllexport) 共用时确保符号可见

noreturn — 标记不返回函数

告知编译器该函数执行后不会返回(如调用 exit()、throw、死循环等),消除”控制流到达函数末尾”的误报警告。

C++cpp
__declspec(noreturn) void FatalError(const char* msg) {
    fprintf(stderr, "Fatal: %s\n", msg);
    exit(1);
}

跨平台等价写法

  • C++11:[[noreturn]]
  • GCC/Clang:__attribute__((noreturn))

align(n) — 内存对齐

指定变量或结构体的内存对齐字节数(必须是 2 的幂)。

C++cpp
__declspec(align(16)) struct AlignedVector {
    float x, y, z, w;
};
 
__declspec(align(64)) char cache_line[64]; // SIMD 友好

跨平台等价写法

  • C++11:alignas(n)
  • GCC/Clang:__attribute__((aligned(n)))

naked — 裸函数(无编译器序言/尾声)

编译器不自动生成函数入口(push ebp / mov ebp, esp)和出口代码,函数体须全部用内联汇编手写。

C++cpp
__declspec(naked) int __fastcall FastHook(int a, int b) {
    __asm {
        // 手写 prologue / epilogue
        push ebp
        mov ebp, esp
        // ... 自定义逻辑
        pop ebp
        ret
    }
}

使用场景: 函数 Hook、shellcode 编写、极致性能优化。
注意:仅支持 x86,x64 不支持 naked。


uuid("...") — 附加 GUID

为 COM 接口或类附加一个全局唯一标识符(GUID),配合 __uuidof() 使用。

C++cpp
__declspec(uuid("6fd5b7e0-44c3-11d1-b5f9-00c04fb9999d"))
struct IMyInterface : IUnknown {
    virtual HRESULT MyMethod() = 0;
};
 
// 获取 GUID
GUID id = __uuidof(IMyInterface);

novtable — 省略虚表指针初始化

用于纯抽象接口类,告知编译器不生成虚表初始化代码(构造/析构中),节省代码体积。

C++cpp
struct __declspec(novtable) IFoo {
    virtual void Bar() = 0;
    virtual ~IFoo() = default;
};

注意

不能直接实例化带有 novtable 的类,否则虚函数调用会崩溃。仅用于纯接口定义。


property — 伪属性(get/set 访问器)

为结构体/类成员定义 getter/setter,使其像普通字段一样被访问(仅 MSVC 支持)。

C++cpp
struct MyRect {
    int left, top, right, bottom;
 
    __declspec(property(get=GetWidth, put=SetWidth)) int Width;
    int GetWidth() const { return right - left; }
    void SetWidth(int w) { right = left + w; }
};
 
MyRect r;
r.Width = 100;    // 调用 SetWidth
int w = r.Width;  // 调用 GetWidth

属性速查表

属性作用跨平台替代
deprecated("msg")标记已弃用,编译警告[[deprecated("msg")]] (C++14)
dllexport从 DLL 导出符号无直接等价 (用 visibility)
dllimport从 DLL 导入符号无直接等价
selectany允许多重定义,链接器取一__attribute__((weak))
noinline禁止内联展开__attribute__((noinline))
noreturn标记不返回函数[[noreturn]] (C++11)
align(n)指定内存对齐alignas(n) (C++11)
naked裸函数(无序言/尾声)__attribute__((naked))
uuid("...")附加 COM GUID无
novtable省略虚表初始化(纯接口)无
propertyget/set 访问器语法糖无

参考