C/C++ 中 `typedef` 关键字详解

1. 基本作用

typedef 用于为已有类型定义别名。

cpp 复制代码
typedef unsigned int uint;

此后:

cpp 复制代码
uint value = 10;

uintunsigned int 是同一种类型,不是创建了一个全新的强类型。


2. 基本语法

cpp 复制代码
typedef 原类型 别名;

示例:

cpp 复制代码
typedef int Integer;
typedef unsigned long SizeType;
typedef const char* CString;

3. 与变量声明语法的关系

typedef 的写法可以理解为:把普通变量声明中的变量名替换成类型别名。

普通声明:

cpp 复制代码
int value;

类型别名:

cpp 复制代码
typedef int ValueType;

指针声明:

cpp 复制代码
int* pointer;

类型别名:

cpp 复制代码
typedef int* IntPointer;

4. 指针类型别名

cpp 复制代码
typedef int* IntPtr;

IntPtr p1;
IntPtr p2;

p1p2 都是 int*

对比:

cpp 复制代码
int* p1, p2;

这里:

  • p1int*
  • p2int

因此指针别名可以减少复杂声明中的歧义,但也可能隐藏指针属性。


5. const 与指针别名陷阱

cpp 复制代码
typedef int* IntPtr;

const IntPtr p = nullptr;

展开后不是:

cpp 复制代码
const int* p;

而是:

cpp 复制代码
int* const p;

因为 IntPtr 整体代表 int*const 修饰的是整个指针类型。

若需要指向常量整数的指针:

cpp 复制代码
typedef const int* ConstIntPtr;

现代 C++ 中使用 using 仍有同样的类型语义:

cpp 复制代码
using IntPtr = int*;
const IntPtr p = nullptr;  // int* const

6. 结构体中的 typedef

C 语言中常见:

c 复制代码
typedef struct Device {
    int id;
    char name[32];
} Device;

之后可直接写:

c 复制代码
Device device;

若没有 typedef

c 复制代码
struct Device {
    int id;
};

struct Device device;

在 C++ 中,结构体标签名本身可以直接作为类型名:

cpp 复制代码
struct Device {
    int id;
};

Device device;

所以 C++ 中通常无需为结构体重复定义同名 typedef


7. 匿名结构体别名

C 风格:

c 复制代码
typedef struct {
    int x;
    int y;
} Point;

使用:

c 复制代码
Point p;

该结构体没有独立标签名,只能通过 Point 使用。

大型工程更推荐给结构体一个明确名称,便于前向声明和调试。


8. 函数指针别名

原始声明较难阅读:

cpp 复制代码
int (*handler)(const char*, int);

使用 typedef

cpp 复制代码
typedef int (*Handler)(const char*, int);

声明变量:

cpp 复制代码
Handler handler = nullptr;

完整示例:

cpp 复制代码
#include <iostream>

typedef int (*BinaryOperation)(int, int);

int add(int a, int b) {
    return a + b;
}

int main() {
    BinaryOperation operation = add;
    std::cout << operation(2, 3);
}

9. 数组类型别名

cpp 复制代码
typedef int IntArray10[10];

IntArray10 values{};

函数参数:

cpp 复制代码
void initialize(IntArray10& values) {
    for (int& x : values) {
        x = 0;
    }
}

注意数组作为普通函数参数时会退化为指针,因此引用数组需要显式写 &


10. 复杂类型简化

cpp 复制代码
typedef std::map<
    std::string,
    std::vector<int>
> IndexTable;

使用:

cpp 复制代码
IndexTable table;

现代 C++ 推荐:

cpp 复制代码
using IndexTable =
    std::map<std::string, std::vector<int>>;

using 的赋值形式通常更易读。


11. typedefusing

等价示例:

cpp 复制代码
typedef unsigned int UInt;
using UInt = unsigned int;

函数指针:

cpp 复制代码
typedef int (*HandlerOld)(int, int);
using HandlerNew = int (*)(int, int);

using 通常更接近"别名 = 类型"的阅读顺序。


12. 别名模板

typedef 不能直接定义模板别名,而 using 可以。

cpp 复制代码
template <typename T>
using Vec = std::vector<T>;

Vec<int> values;

不能写成简单的:

cpp 复制代码
template <typename T>
typedef std::vector<T> Vec;  // 非法

旧式代码可通过包装结构间接实现:

cpp 复制代码
template <typename T>
struct VecAlias {
    typedef std::vector<T> type;
};

VecAlias<int>::type values;

因此现代 C++ 中一般优先使用 using


13. 类内部类型别名

cpp 复制代码
class DeviceManager {
public:
    typedef std::uint32_t DeviceId;
    typedef std::vector<DeviceId> DeviceIdList;
};

使用:

cpp 复制代码
DeviceManager::DeviceId id = 100;

现代写法:

cpp 复制代码
class DeviceManager {
public:
    using DeviceId = std::uint32_t;
    using DeviceIdList = std::vector<DeviceId>;
};

14. 模板中的 typename

cpp 复制代码
template <typename Container>
void process(const Container& container) {
    typedef typename Container::value_type ValueType;

    ValueType value{};
}

为什么需要 typename

Container::value_type 依赖模板参数,编译器默认不能确定它是类型还是静态成员,因此需要用 typename 明确说明。

现代写法:

cpp 复制代码
template <typename Container>
void process(const Container& container) {
    using ValueType = typename Container::value_type;
    ValueType value{};
}

15. STL 中常见类型别名

容器通常定义:

cpp 复制代码
Container::value_type
Container::size_type
Container::iterator
Container::const_iterator
Container::reference
Container::const_reference

示例:

cpp 复制代码
std::vector<int>::value_type value = 1;
std::vector<int>::size_type size = 10;

泛型代码可使用:

cpp 复制代码
template <typename Container>
void printSize(const Container& container) {
    typename Container::size_type size = container.size();
}

更简洁:

cpp 复制代码
auto size = container.size();

16. 类型别名不创建新类型

cpp 复制代码
typedef int UserId;
typedef int DeviceId;

void process(UserId id);

以下调用仍然合法:

cpp 复制代码
DeviceId device_id = 10;
process(device_id);

因为两者本质上都是 int

需要强类型隔离时,应定义包装类型:

cpp 复制代码
struct UserId {
    int value;
};

struct DeviceId {
    int value;
};

或使用专门的 strong typedef 工具模式。


17. API 可读性

cpp 复制代码
using TimestampNs = std::int64_t;
using FrameId = std::uint64_t;
using Confidence = float;

函数签名:

cpp 复制代码
FrameId processFrame(
    TimestampNs timestamp,
    Confidence threshold
);

别名有助于表达语义,但不能阻止不同底层相同类型之间误传。


18. 前向声明与别名

普通类可以前向声明:

cpp 复制代码
class Device;

但无法仅通过一个不完整声明随意复现复杂模板别名。若别名是公共 API 的一部分,应将其放在稳定的公共头文件中。


19. 与 decltype 配合

cpp 复制代码
using ReturnType = decltype(function(args...));

模板中:

cpp 复制代码
template <typename F, typename... Args>
using InvokeResult =
    std::invoke_result_t<F, Args...>;

标准库中大量 _t 形式都是别名模板,例如:

cpp 复制代码
std::remove_reference_t<T>
std::decay_t<T>
std::enable_if_t<Condition, Type>

20. 编码建议

  1. C 代码中合理使用 typedef 简化结构体和函数指针。
  2. 现代 C++ 优先使用 using
  3. 别名命名应表达语义,而不只是缩短字符。
  4. 不要用类型别名掩盖危险的裸指针所有权。
  5. 注意 const Alias 修饰的是整个别名类型。
  6. 需要类型安全时使用包装类,而不是普通别名。
  7. 公共 API 中保持别名稳定,避免频繁暴露底层实现。
  8. 模板依赖类型前记得使用 typename
  9. 别名模板使用 using,不要尝试用 typedef 直接实现。
  10. 函数指针复杂时,也可以考虑 std::function,但需评估其运行时开销和所有权语义。

21. 面试总结

typedef 为已有类型创建别名,不会创建新的独立类型。它可用于简化指针、数组、函数指针和复杂模板类型声明。

现代 C++ 中,using 通常可读性更好,并且支持别名模板,因此一般优先使用 using。使用指针别名时要特别注意 const 修饰的是整个别名类型。

相关推荐
会周易的程序员3 小时前
js-shm: 高性能 Node.js 共享内存模块
开发语言·javascript·c++·node.js·共享内存·shm
2023自学中3 小时前
imx6ull 开发板 贪吃蛇, C++11 SDL2 无硬件GPU优化版
linux·c++
anscos4 小时前
为CUDA 代码引入静态分析
c++·工业软件·功能检测
众少成多积小致巨4 小时前
C++ 规范参考(中)
c++
小保CPP4 小时前
OpenCV C++基于AI模型的场景文本识别(OCR)
c++·人工智能·opencv·计算机视觉·ocr
shylyly_4 小时前
C++中的类型转换
开发语言·c++·匿名对象·隐式类型转换·拷贝优化
jinyishu_4 小时前
哈希表原理与开放定址法C++实现
c++·哈希算法·散列表
小龙报5 小时前
【优选算法】1. 水果成蓝 2.找到字符串中所有字母的异位词
java·c语言·数据结构·数据库·c++·redis·算法
txzrxz6 小时前
数论:排列数、组合数、费马小定理、逆元、同余定理
c++·算法·数论·组合数·费马小定理·逆元·排列数