C++ 怎么定义函数指针 C++ typedef与using定义回调函数类型【语法】

8次阅读

函数指针是存储函数入口地址的变量,定义需严格匹配返回类型、参数列表及限定符(如const/noexcept/调用约定);推荐using定义回调类型,支持模板别名且可读性高。

C++ 怎么定义函数指针 C++ typedef与using定义回调函数类型【语法】

怎么写一个指向函数的指针变量

函数指针本质是变量,它存储的是函数的入口地址,不是函数本身。定义时必须和目标函数的返回类型、参数个数与类型完全一致。

比如有函数 int add(int a, int b),它的函数指针类型int (*)(int, int);声明变量就得写成:

int (*func_ptr)(int, int) = add;

注意括号不能省:int *func_ptr(int, int) 是函数声明(返回 int*),不是函数指针。

常见错误:

立即学习C++免费学习笔记(深入)”;

  • 漏掉最外层括号,导致被解析为函数声明
  • 参数类型写错,比如把 const std::String& 写成 std::string,编译失败
  • auto 推导时忘记加 &*,比如 auto p = &add 正确,auto p = add 会退化为函数类型而非指针

typedef 和 using 哪个更适合定义回调类型

using 更推荐——语法更直观、支持模板别名、可读性高;typedef 在复杂声明里容易绕晕人,尤其嵌套函数指针时。

对比定义同一类型:

typedef int (*callback_t)(double, const char*);
using callback_t = int (*)(double, const char*);

两者等价,但 using 的等号形式更贴近“变量赋值”直觉,且能轻松扩展为模板别名:

template
using handler_t = void (*)(T, bool);
handler_t h1 = [](int x, bool ok) { /* ... */ }; // OK

typedef 无法直接做这种泛型绑定。

定义带 const / noexcept / 引用限定符的函数指针类型

如果目标函数带 noexceptconst 成员限定或右值引用限定,函数指针类型也必须严格匹配,否则编译报错。

例如类成员函数

struct Foo {
void method() const noexcept { }
};
void (Foo::*ptr)() const noexcept = &Foo::method;

普通函数若声明为 void f() noexcept,其指针类型就是 void (*)() noexcept;漏掉 noexcept 就不兼容。

关键点:

  • 成员函数指针必须用 ClassName::* 语法,且要带上 const/volatile/&/&& 限定符
  • 普通函数的 noexcept 是类型系统一部分,不是修饰符,必须写在函数指针声明里
  • c++17 起,函数类型中的 noexcept 影响重载决议和模板推导

回调函数类型定义中容易忽略的 ABI 兼容问题

windows 上 __stdcall__cdecl 等调用约定是类型的一部分。用错会导致损坏或崩溃,但编译器不一定报错。

例如 Win32 API 的 WNDPROC 实际是:

using WNDPROC = LRESULT (CALLBACK*)(HWND, UINT, WPARAM, LPARAM);

其中 CALLBACK 展开为 __stdcall。如果你用 using my_proc = LRESULT (*)(HWND, UINT, WPARAM, LPARAM)(默认 __cdecl)去赋值,运行时可能 crash。

所以:

  • 跨平台库尽量避免显式调用约定,依赖默认
  • 对接系统 API 时,务必查文档确认调用约定,并在类型定义中显式写出(如 __stdcall
  • Clang/GCC 下可用 __attribute__((stdcall)) 模拟,MSVC 下用 __stdcall

函数指针的“类型安全”比看起来更脆弱——差一个 const、一个 noexcept、一个调用约定,都可能导致未定义行为,而编译器有时只给弱警告。

text=ZqhQzanResources