在 C++ 中,using
是一个关键字,用来为已有数据类型创建别名。语法为:
using 别名 = 原类型;
例如:
using Distance = double; // 将 Distance 定义为 double 的别名
定义完成后,即可在任何需要类型的地方使用该别名:
Distance milesToDestination{ 3.4 }; // 实际类型为 double
编译器遇到别名时,会用原类型替换。示例:
#include <iostream>
int main()
{
using Distance = double;
Distance milesToDestination{ 3.4 };
std::cout << milesToDestination << '\n'; // 输出 3.4
return 0;
}
供进阶读者
类型别名亦支持模板化,见 13.14 课 —— 类模板实参推导(CTAD)与推导指引。
类型别名的命名惯例
历史上命名方式并不统一,常见有三种:
- 以
_t
结尾(如size_t
、nullptr_t
),继承自 C,但现代 C++ 已不推荐;POSIX 保留全局_t
后缀,可能产生冲突。 - 以
_type
结尾(如std::string::size_type
),但同样存在不一致。 - 无后缀,首字母大写。现代 C++ 推荐用 PascalCase 命名自定义类型,以区分变量/函数名(小写开头)并避免碰撞。
示例:
void printDistance(Distance distance);
Distance
是类型,distance
是形参名,C++ 区分大小写,合法且清晰。
最佳实践
自定义类型别名使用首字母大写且无后缀(除非有特定理由)。
作者注
本教程部分旧课仍用 _t
/_type
,欢迎指出以便统一。
类型别名并非新类型
别名只是为原类型引入新标识符,不创建独立类型,二者完全可互换。因此可能出现语法正确但语义荒谬的代码:
int main()
{
using Miles = long;
using Speed = long;
Miles distance{ 5 };
Speed mhz{ 3200 };
distance = mhz; // 合法,但语义错误
}
编译器仅看到 long
到 long
,不会阻止此类错误,故别名不具备类型安全;尽管如此,它们仍有益处。
警告
务必避免混用语义本应不同却共享同一别名的值。
附注
部分语言支持强 typedef(strong typedef),会生成真正的新类型,混用时报错。C++20 无原生支持,但可用第三方库实现;enum class
与之类似(见 13.6 课)。
类型别名的作用域
作用域规则与变量相同:块内定义的别名仅在该块可见;全局定义则文件内可见。若需在多文件使用,可放在头文件:
mytypes.h
#ifndef MYTYPES_H
#define MYTYPES_H
using Miles = long;
using Speed = long;
#endif
通过 #include
后,别名即拥有全局作用域。
typedef
typedef
是早期创建别名的方式,语法:
typedef long Miles; // 与 using Miles = long; 等效
为兼容 C 仍保留,但现代 C++ 推荐 using
。
typedef
语法缺陷:
- 顺序易错:
typedef Distance double; // 错 typedef double Distance; // 对
- 复杂类型可读性差:
typedef int (*FcnType)(double, char); // 难读 using FcnType = int(*)(double, char); // 易读
- 名称“typedef”暗示“定义新类型”,实为别名。
最佳实践
优先使用类型别名(using
)而非 typedef
。
术语
- C++ 标准用 typedef names 统称 typedef 与类型别名。
- 日常中“typedef”常泛指两者,因功能一致。
何时使用类型别名
平台无关编码
int
、long
等大小随平台变化。跨平台程序常用别名包含位宽信息,如int8_t
、int16_t
、int32_t
,配合预处理器:#ifdef INT_2_BYTES using int8_t = char; using int16_t = int; using int32_t = long; #else using int8_t = char; using int16_t = short; using int32_t = int; #endif
标准库中的固定宽度整数(
std::int16_t
、std::uint32_t
)和size_t
亦为此类别名。简化复杂类型
例如:using VectPairSI = std::vector<std::pair<std::string, int>>;
大幅减少打字与错误。
描述值语义
函数返回裸int
时语义不明:using TestScore = int; TestScore gradeTest();
虽仅适用于大量相关函数,否则用注释更佳。
便于维护
若需将short
改为long
,只需修改别名:using StudentId = long; // 原为 short
但跨类型族修改需重测代码!
缺点与结论
别名引入额外标识符,若未提升可读性/可维护性则弊大于利。
糟糕别名会把熟悉类型(如 std::string
)藏在新名字后,甚至隐藏智能指针语义,反而有害。
最佳实践
仅在显著提升可读性或可维护性时使用别名;多位置使用优于少量使用。
小测验
问题 #1
给定函数原型:
int printData();
将其返回类型改为类型别名 PrintError
,请写出类型别名定义及更新后的函数原型。
(参考答案略)