1. 基本作用
typedef 用于为已有类型定义别名。
cpp
typedef unsigned int uint;
此后:
cpp
uint value = 10;
uint 与 unsigned 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;
p1 和 p2 都是 int*。
对比:
cpp
int* p1, p2;
这里:
p1是int*;p2是int。
因此指针别名可以减少复杂声明中的歧义,但也可能隐藏指针属性。
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. typedef 与 using
等价示例:
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. 编码建议
- C 代码中合理使用
typedef简化结构体和函数指针。 - 现代 C++ 优先使用
using。 - 别名命名应表达语义,而不只是缩短字符。
- 不要用类型别名掩盖危险的裸指针所有权。
- 注意
const Alias修饰的是整个别名类型。 - 需要类型安全时使用包装类,而不是普通别名。
- 公共 API 中保持别名稳定,避免频繁暴露底层实现。
- 模板依赖类型前记得使用
typename。 - 别名模板使用
using,不要尝试用typedef直接实现。 - 函数指针复杂时,也可以考虑
std::function,但需评估其运行时开销和所有权语义。
21. 面试总结
typedef 为已有类型创建别名,不会创建新的独立类型。它可用于简化指针、数组、函数指针和复杂模板类型声明。
现代 C++ 中,using 通常可读性更好,并且支持别名模板,因此一般优先使用 using。使用指针别名时要特别注意 const 修饰的是整个别名类型。