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 |
|