using 的三大作用汇总
前言
在 C++ 中,初学者可能经常有一个疑惑:using 到底是干嘛的?using 的功能总有一种覆盖面广的感觉,似乎并没有特定的功能。其实 using 的功能主要可以总结为两个 “引入”、一个 “别名”。其中两个引入是指 using namespace `namespace` 和 using `namespace`::`member` ,一个别名是指 using `alias` = `typename` 。但实际上又并非这么简单,using 在类中又被赋予了更多的意义,本文将就上面讲到的这些内容作整理总结。
using namespace `namespace`
在 C++ using 的三种用法当中,属 using namespace 的用法最纯粹、简单。
1 |
|
在上面这个示例中,我们使用 using namespace 将命名空间 std 中内容全部引入到当前命名空间(根空间)中,这里命名空间 std 是标准库的空间,所有 C++ 的标准方法都在这个空间中。
因为 std 这个命名空间在当前源文件很常用,所以我们将其全部引入,否则我们每次必须使用第 6 行的内容替代第 5 行,显得非常麻烦。当然,使用 using namespace 也会带来一些命名空间污染的问题:
1 |
|
在这个示例中 move(10) 会调用本源文件的 move,而这也许会和我们期望的结果不同。当然,索性的是,我们依然可以使用 std::move 显式调用。在这里命名空间污染是十分危险的,因为你不知道你导入的包中是否引入其他命名空间,总之尽量不要使用 using namespace 。
关于 std::move
std::move 用于将左值转换为右值,用于提供移动语义。根据 C++ Primer 介绍,始终建议使用 std::move 而非 move,始终不要引入 std::move 到本地命名空间,正是出于命名空间可能存在污染的原因。
using `namespace`::`member`
引入一般作用域
与 using namespace 类似,using namespace::member 提供了将其他作用域引入当前作用域的操作。不同的是 using namesapce::member 提供了更加细腻的操作,用于应对 using namespace 存在命名空间污染的问题。当然,同样地,using namespace::member 也存在命名空间污染问题,但其操作更细腻可控。
1 |
|
在上面这个示例中,我们显式的引入了 std::cin / std::cout / std::endl,而不会引入 std 命名空间中的其他成员,这相对 using namespace std; 更加安全,例如就不存在 std::move 污染的问题。
C++ 17 更新
从 C++ 17 开始,允许使用 , 分隔,同时引入多个命名空间成员。
1 | using std::cin, std::cout, std::endl; |
引入继承体系(类域)
在继承体系中,using namesapce::member(using classscope::member)被赋予了更多意义,它可以改变成员在派生类中的访问权限。
1 | class Base { |
根据继承体系,在类 Derived 中,int foo(); 的访问权限是 public 的,而 int foo(int); 的访问权限是 protected 的。而使用 using 则可以改变其访问权限,例如 Case 1 将 foo 的两个重载都指定为 public 的,而 Case 2 则将 foo 的两个重载都指定为 protected 的。
需要注意的一点是,如果不使用 using,则新增的 double foo(double) 函数将覆盖原来的两个函数,此时不会发生重载;如果使用 using,则新增的函数将成为 foo 的第三个重载。这一点的表现和一般作用域实际上相同。
引入构造函数
using classscope::member 的体系可谓烦杂,在引入构造函数时(C++ 11 新增),其相对引入继承体系具有新的特殊性。
1 | class Base { |
- 只能引入直接基类的构造函数,例如
class DD : public Derived { using Base::Base; }是非法的。 - 引入构造函数不会改变构造函数的访问权限,也就是说
Base::Base使用在哪个权限下都是一样的,而且构造函数也是默认不继承的(可以使用using显式引入),这一点与友元类似。 - 引入构造函数不会引入其特殊成员函数,包括:默认构造函数,拷贝构造函数、移动构造函数。这些特殊成员函数使用其自己的合成方式自动合成。
using `alias` = `typename`
using alias = typename 与前两者都不同,它用于给类型提供别名(C++ 11 新增)。
一般类型别名
作为类型别名,它与 typedef 类似。
1 | using i8 = signed char; |
类中同样可以使用类型别名,只是它存在访问权限的限制。在类中,它被称为类型成员,是静态成员。
模板类型别名
在类型别名中也支持模板,例如下面的示例。
1 |
|
