( Owed by: 春夜喜雨 http://blog.csdn.net/chunyexiyu )
1、C++ 类型定义:using / typedef / #define / constexpr 对比
using、typedef、#define、constexpr 是 C++ 给类型或值起别名的核心关键字。
using/constexpr:C++11 现代写法,强烈推荐typedef:老式写法,仅兼容旧代码#define:纯文本替换,严禁用于类型/常量
下面从用途、原理、作用域、安全性方面,列表对比来看。
列表对比
| 关键字 | 本质 | 用途 | 作用域 | 类型安全 | 推荐度 |
|---|---|---|---|---|---|
using |
C++11 现代别名 | 类型别名(首选) | 编译期、作用域安全 | ✅ 强类型安全 | ⭐⭐⭐⭐⭐ |
typedef |
C 老式别名 | 类型别名(兼容旧代码) | 编译期、作用域安全 | ✅ 强类型安全 | ⭐⭐⭐ |
#define |
纯文本宏替换 | 文本替换(极不推荐用于类型) | 全局、无作用域 | ❌ 无类型、易出BUG | ⭐ |
constexpr |
编译期常量表达式 | 常量值定义 | 编译期、作用域安全 | ✅ 强类型安全 | ⭐⭐⭐⭐⭐ |
归纳总结:
- 定义类型别名 → 优先用
using;(函数指针也用它) - 定义编译期常量 → 用
constexpr,替代define方式的常量定义; - 绝对不要用
#define定义类型/常量; - typedef` 仅用于兼容旧代码。
定义类型别名
定义编译期常量
禁止操作
兼容场景
开始:需要定义类型/常量
需求类型
优先使用 using
使用 constexpr
绝对不要用 #define 定义类型
typedef 仅用于兼容旧代码
结束
2、代码样例
-
- 类型别名 → 用 using(推荐)
-
- 编译期常量 → 用 constexpr
-
- ❌ 禁止:#define 定义类型
-
- typedef(仅兼容旧代码)
c
#include <array>
#include <vector>
#include <map>
#include <string>
// ==========================
// 1. 类型别名 → 用 using(推荐)
// ==========================
using MyInt = int;
using StrMap = std::map<std::string, std::string>;
// 函数指针(最清晰写法)
using FuncPtr = void(*)(int, double);
// 模板别名(using 独有功能)
template <typename T>
using MyVec = std::vector<T>;
// ==========================
// 2. 编译期常量 → 用 constexpr
// ==========================
constexpr int MAX_COUNT = 100;
constexpr double PI = 3.14159;
constexpr bool IS_DEBUG = true;
// 用于数组大小 / 模板参数
constexpr int ARR_SIZE = 5;
int arr[ARR_SIZE]; // 数组大小
std::array<int, ARR_SIZE> arr_std; // 模板参数
// ==========================
// 3. ❌ 禁止:#define 定义类型
// ==========================
// #define MyInt int* // 纯文本替换,极易出 Bug
// ==========================
// 4. typedef(仅兼容旧代码)
// ==========================
typedef int OldInt;
typedef void(*OldFuncPtr)(int);
3、语法详解
3.1. using(C++11 现代类型别名)
最推荐、最强大、语法最直观 ,完全替代 typedef。
- 语法:
新名字 = 旧类型,直观易懂 - 支持函数指针、模板别名(typedef 做不到)
- 作用域安全:在类/函数内定义只在内部生效
- 编译期处理,无运行时开销
用法
cpp
// 给类型起别名
using MyInt = int;
using StrVector = std::vector<std::string>;
// 函数指针别名(比 typedef 好读 10 倍)
using FuncPtr = int(*)(int, int);
// 模板别名(typedef 做不到!)
template <typename T>
using Vec = std::vector<T>;
3.2. constexpr(编译期常量表达式)
不是类型别名 ,是定义编译期常量 ,常用来替代define方式的常量定义,例如定义PI值,均应优先使用constexpr方式。
用途:数组大小、模板参数、全局常量配置
特点:
- 类型安全,作用域安全
- 编译期求值,无运行时开销
- 用于常量值,不是给类型起别名
用法
cpp
constexpr int MAX = 1024;
constexpr int square(int x) { return x*x; }
constexpr int res = square(10); // 编译期直接算出 100
3.3. typedef(老式类型别名)
C 语言遗留,C++ 为了兼容保留,语法不易懂,不推荐新代码使用。
- 语法顺序混乱:
typedef 旧类型 新名字 - 不支持模板别名
- 复杂类型可读性极差(如函数指针、嵌套类型)
cpp
// 等价于 using MyInt = int;
typedef int MyInt;
// 函数指针(可读性差),等价于using FuncPtr = int(*)(int, int);
typedef int(*FuncPtr)(int, int);
3.4. define定义(纯文本宏,危险!)
预处理阶段文本替换 ,不是类型定义 ,绝对不要用来定义类型。
- 预处理阶段替换,不是 C++ 语法
- 无作用域、无类型检查、极易产生隐蔽 Bug
cpp
#define MyInt int*
MyInt a, b;
// 展开后:int* a, b;
// a 是指针,b 是普通 int!完全错误
4、关键的区别~面试常问
4.1. using vs typedef(都是类型别名)
using语法更清晰,支持模板,是现代 C++ 标准typedef语法不易懂,无模板支持,仅用于兼容旧代码
规则 :新项目一律用 using,不用 typedef。
4.2. 宏 #define 真的危险吗?
因为define是纯文本替换,不是类型定义,会产生难以发现的 Bug。
非常危险!看这个经典BUG:
cpp
#define MyInt int*
MyInt a, b; // a 是指针,b 是 int!
using MyInt = int*;
MyInt a, b; // a、b 都是指针,正确!
4.3. constexpr 到底干嘛的?
它不定义类型别名 ,只定义编译期就能确定的值 :
用于:
- 数组大小
- 模板参数
- 常量配置
示例:
cpp
// 1. 数组大小
constexpr int N = 10;
int arr[N]; // 合法,N 是编译期常量
// 2. 模板参数
constexpr int ARRAY_SIZE = 5;
std::array<int, ARRAY_SIZE> my_array;
// 3. 常量配置
constexpr double PI = 3.1415926;
// 不建议的方式
#define N 10
int arr[N]; // 也能用,但不安全、无类型
5、总结实践
5.1 推荐用法
cpp
#include <vector>
#include <map>
#include <array>
#include <string>
// 类型别名
using UserId = int;
using UserMap = std::map<std::string, UserId>;
using LogicFunc = bool(*)(int);
// 模板别名
template <typename T>
using MyArray = std::vector<T>;
// 编译期常量
constexpr int MAX_USER = 1024;
constexpr double CIRCLE_RATE = 3.1415926;
constexpr int BUF_SIZE = 64;
// 编译期常量用于容器模板参数
std::array<int, BUF_SIZE> buf;
5.2 总结
- 给类型起别名:优先
using,完全替代typedef - 定义编译期常量:用
constexpr - typedef:仅维护旧代码时使用,新项目不用
- 文本宏define:绝不用于类型/常量,只在极端底层场景使用