C++ 关键字 :using

news2024/11/21 0:31:56

        

        

        在 C++ 庞大语法体系中, using 关键字十分的灵活多用,它可不简单。

        除了常规的引入命名空间之外,它还可用于引入枚举类型枚举器、定义常规类型别名、模板类型别名等。在定义常规类型别名方面与C语言中的typedef、#define与之相似,但又不尽相同。

        简单归纳起来, using 的主要作用是 “引入” “定义别名”。

        

引入

引入命名空间       

using namespace ns-name ;

        引入命名空间是最常见也最好理解的用法,如:

#include <iostream>
using namespace std;

int main() {
    cout << "Hello World!" << endl;
    // std::cout << "Hello World!" << endl;
    return 0;
}

         在这个示例中,我们使用 using namespace 将命名空间 std 的全部成员引入到当前命名空间(根空间)中。

        这样做的好处是,代码变得简洁,有效作用域内,都可以省略命名空间,直接访问。

        有效作用域是指从 using 声明点开始,直到包含该using声明的作用域结尾。

 using 关键字可以引入整个命名空间或者特定的命名空间成员。

        该怎样直观的理解这句话呢 ,看如下代码:

namespace
{
    int i; // 定义 ::(unique)::i
}
 
void f()
{
    i++;   //  定义 ::(unique)::i
}
 
namespace A
{
    namespace
    {
        int i;        // A::(unique)::i
        int j;        // A::(unique)::j
    }
 
    void g() { i++; } // A::(unique)::i++
}
 
using namespace A; //  将 A 中的所有名称引入全局名称空间
 
void h()
{
    i++;    // error: ::(unique)::i and ::A::(unique)::i 都在作用域中
    A::i++; // ok, 递增 ::A::(unique)::i
    j++;    // ok, 递增 ::A::(unique)::j
}

        由例子可见,因为引入了命名空间A,其下的所有成员都将暴露出来。变量 i  在两个命名空间中都有声明,因此产生了冲突。

        因此,在.h头文件中,一般不应该使用using声明,避免产生名字冲突。

        方便的同时,也带来了问题,如命名空间污染。        

#include <iostream>
#include <utility>
using namespace std;

void move(int &&x) {
    cout << "Move: " << x << endl;
}

int main() {
    move(10);  // 调用 void move(int &&x);
    std::move(10);  // 调用 std::move
    return 0;
}

        在这个示例中 move(10) 会调用本源文件的 move,也许这并不是你的预期。当然,我们依然可以使用 std::move 显式调用。

        💡 命名空间污染是十分隐晦和危险的,特别是当多个命名空间中有相同名称的成员时,需要特别的注意。

        另外,这里还有一个“后续扩展成员不可见的”规则,即在 using 后访问的命名空间,无法访问到后续扩展命名空间时新增的成员,有说起来点绕,看示例:        

namespace A
{
	int i;
}

int main()
{
	A::i++; // ok
	A::j++; // error
}

namespace A
{
	int j;
}

        i 在 using 之前就定义了,可以正常访问, j 在using、访问之后定义,不可见。

        由此规则,可能会衍生一个潜在的混淆: 

namespace A
{
	void f(int) {}; // 定义一个接受int参数的函数f
}
using A::f; // 使得全局命名空间中的::f成为A::f(int)的同义词

// 扩展命名空间A
namespace A
{
	void f(char) {}; // 定义一个接受char参数的函数f,不改变全局命名空间中::f的含义
}


int main()
{
	f('a');     // 调用 void f(int),参数隐式转换
	using A::f; // 重新引入完整的命名空间A
	f('a');     // 调用 void f(char) 
}

        一个例外是函数模板特化:如果引入的是类模板,后续添加的特化版本是可见的,因为它们的查找是通过主模板进行的。

引入命名空间的实体

       

using ns-name :: member-name ;

        相对于引入整个命名空间,引入命名空间内的实体操作更为细腻,如:

#include <iostream>
using std::cin;
using std::cout;
using std::endl;

int main() {
    cout << "Hello World!" << endl;
    // std::cout << "Hello World!" << endl;
    return 0;
}

        在这个示例中,我们只引入了 std命名空间中的cin、cout、endl 实体成员。当然这也仅仅是降低了命名空间污染的可能性,最终,需要我们自己审慎把握。

💡 从 C++ 17 开始,允许使用 分隔,同时引入多个命名空间成员。

using std::cin, std::cout, std::endl;

引入类的继承体系

        

using classscope::member;

       首先,在类的继承体系中,using 使得派生类显式地重新暴露基类的被隐藏或覆盖的成员函数,使得基类的成员函数在派生类的作用域内重新可用。


class Base {
public:
	virtual void foo() { /*...*/ }
	void bar() {}
};

class Derived : public Base {
public:
	void foo() override { /*...*/ } // 重写foo

	using Base::foo; // 显式再次引入Base的foo版本

	void bar() {}
	using Base::bar;
};

int main()
{
	Derived d;
	d.foo();		// 调用 Derived::foo();
	d.Base::foo();	// 调用被覆盖的	Base::foo();

	d.bar();		// 调用 Derived::bar();
	d.Base::bar();	// 调用被隐藏的 Base::bar();
}

        其次,在子类私有继承时,可以改变成员的可见性,如:

class Base {
public:
    void foo() {}
	void bar() {}
};

class Derived : private Base {
public:
	using Base::foo;    // 显式引入Base的foo
	//using Base::bar;  // 未引入Base的bar
};

int main()
{
	Derived d;
	d.foo();		// Base::foo() 可访问
	d.bar();		// Base::bar() 可访问
}

继承构造函数 (C++11)

派生类可以使用 using 声明从直接基类继承构造函数

        在C++11之前,如果基类有一个构造函数需要参数,那么在派生类中必须显式地调用这个构造函数,当基类的构造函数参数很多、版本很多的时候,这将是一件繁重、容易出错的工作。

        现在,使用 using 即可全部引入。

#include <iostream>
using namespace std;

class Base
{
public:
    Base() { cout << "Base()" << endl; }
    Base(const Base& other) { cout << "Base(Base&)" << endl; }
    explicit Base(int i) : num(i) { cout << "Base(int)" << endl; }
    explicit Base(char c) : letter(c) { cout << "Base(char)" << endl; }

private:
    int num;
    char letter;
};

class Derived : Base
{
public:
    // 继承基类所有构造函数
    using Base::Base;

private:
    // 不能从构造函数初始化成员,需要手动初始化
    int newMember{ 0 };
};

int main()
{
    cout << "Derived d1(5) calls: ";
    Derived d1(5);
    cout << "Derived d1('c') calls: ";
    Derived d2('c');
    cout << "Derived d3 = d2 calls: " ;
    Derived d3 = d2;
    cout << "Derived d4 calls: ";
    Derived d4;
}

观察输出

Derived d1(5) calls: Base(int)
Derived d1('c') calls: Base(char)
Derived d3 = d2 calls: Base(Base&)
Derived d4 calls: Base()

         这种方式大幅减轻了继承基类构造函数的书写。另外,由于这是隐式声明继承的,假设一个继承构造函数不被相关的代码使用,编译器不会为之产生真正的函数代码,更加节省目标代码空间。

        缺憾就是不能方便的同时初始化成员,需要手动初始化。

💡 只能引入直接基类的构造函数,例如 class DD : public Derived { using Base::Base; } 是非法的。

引入枚举类型(C++20

        可以通过 using 引入到枚举类型枚举器名称,效果类似于每个枚举器定义在了作用域中,如

enum class fruit { orange, apple };
 
struct S
{
    using enum fruit; // OK: 将 orange 和 apple 引入 S
};
 
void f()
{
    S s;
    s.orange;  // OK: fruit::orange
    S::orange; // OK: fruit::orange
}

         可以通过 using多次引入多个枚举类型,但如果枚举器名称有重复将产生冲突:

enum class fruit { orange, apple };
enum class color { red, orange };
 
void f()
{
    using enum fruit;    // OK
    // using enum color; // error: color::orange and fruit::orange conflict
}

定义别名

定义类型别名

using alias = typename;

       在 C/C++ 中,定义类型别名的方法有 #define、typedefusing 多种方式。

        #define 是宏定义关键字,用途广法,尤其是 C 中,不局限于类型别名定义。

  在C++中,推荐使用 using 来定义类型别名,因为它更符合C++的现代编程风格。

        首先,直观的感受下三者在定义类型别名时的形式,typedef 的方式和其他两者顺序相反:

#define MY_INT int

typedef int MyInt;

using MyInt = int;

         似乎三者没有明显区别,当然由于 #define 宏定义的本质,和后两者是可以明显区分开的。那么,typedef 和 using 的区别在哪里呢?

   首先,使用typedef定义的别名和使用using定义的别名在语义上是等效的。

   然后,我们看下一个定义类型别名的例子:

typedef std::unique_ptr<std::unordered_map<std::string, std::string>> UPtrMapSS;


using UPtrMap = std::unique_ptr<std::unordered_map<std::string, std::string>>;

         using 的方式是不是更为直观易懂?

        当然,也许你很习惯 typedef 的方式,这个示例还不足以让你倒向 using ,那我们继续。

定义模板类型别名

        typedef 是不支持定义模板类型别名的,例如

template <typename T>
typedef map<int, T> type;	// error, 语法错误

        要实现这一点,需要一个类辅助,这样就很麻烦了。using 可以做到:

template <typename T>
using mymap = map<int, T>;

       typedef 为什么不可以呢,在 n1449 中有这样一段话:

"we specifically avoid the term “typedef template” and introduce the new syntax involving the pair “using” and “=” to help avoid confusion: we are not defining any types here, we are introducing a synonym (i.e. alias) for an abstraction of a type-id (i.e. type expression) involving template parameters."

        所以,这事标准委员会的观点与选择,涉及到模板类型时,我们必须使用 using 
 

总结

        using  关键字为程序书写、定义别名带来了极大的方便,使得代码更为简练,善用 using  ,使得代码更为简洁易懂。

        在 C++11 后续的语言标准中又增加了更多的适用场景如,变量模板(C++14)、类模板的默认实参和推导实参(C++17)、委托转隶构造函数(使用using)(C++20),最后以一段  特性大杂烩作为结束

// 类型别名声明 (C++11)
using IntVector = std::vector<int>;

// 模板别名 (C++11)
template <typename T>
using Pair = std::pair<T, T>;

// 委托构造函数 (C++11)
class Foo {
public:
    using FooBase::FooBase; // 委托给基类的构造函数
};

// 继承构造函数 (C++11)
class Base {
public:
    Base(int i) {}
};

class Derived : public Base {
public:
    using Base::Base; // 使用基类的构造函数
};

// 构造函数的显式使用声明 (C++11)
struct A {
    A(int) {}
};

struct B : A {
    using A::A; // 显式使用A的构造函数
};

// 变量模板 (C++14)
template<typename T>
T template_var = 10;

// 类模板的默认实参和推导实参 (C++17)
template <typename T = int>
struct C {};

// 委托转隶构造函数 (C++20)
struct D : B {
    using B::B; // 委托给另一个构造函数
};

 


参考

C++ keyword: using - cppreference.com

Constructors (C++) | Microsoft Learn

http://isocpp.open-std.org/JTC1/SC22/WG21/docs/papers/2003/n1449.pdf

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1674978.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

jupyter notebook中调整图片大小

截屏 ctrl V 这个目前只能保证是截屏大小&#xff0c;改变不了&#xff0c;要么之久 把图形缩小后再截图 感觉很模糊 png文件导入 markdown 代码1 <img src"./1.png" width250 height200>markdown 代码2 <img src"./1.png" width938 height…

文献速递:多模态深度学习在医疗中的应用--多模式婴儿脑分割技术:模糊引导深度学习

Title 题目 Multimodal Infant Brain Segmentation by Fuzzy-informed Deep Learning 多模式婴儿脑分割技术&#xff1a;模糊引导深度学习 01 文献速递介绍 日益普及的非侵入式婴儿脑磁共振图像&#xff08;MRI&#xff09;为准确理解脑主要发展轨迹的动态性提供了机会&…

普中STM32F103ZET6开发板让DS0和DS1两个LED同时亮

欢迎关注我👆,收藏下次不迷路┗|`O′|┛ 嗷~~ 目录 一.前言 二.代码 三.运行效果 一.前言 在这套stm32教程中,只教学了如何亮DS0,而没有教学如何亮DS1。 二.代码 main.c #include "stm32f10x.h"void Syst

一篇文章告诉你聚类算法的作用和怎么用聚类算法!建议收藏!(下篇)

EM聚类 EM聚类 基于概率分布对数据进行建模&#xff0c;通过迭代的期望和最大化步骤来估计模型参数&#xff0c;并将数据分为不同的聚类。EM聚类通常用于处理混合分布的数据&#xff0c;其中每个聚类被建模为一个概率分布。 原理介绍 EM聚类的核心思想是将数据集中的每个样本…

二分答案(区间范围)

D. Jumping Through Segments 输入数据 4 5 1 5 3 4 5 6 8 10 0 1 3 0 2 0 1 0 3 3 3 8 10 18 6 11 4 10 20 0 5 15 17 2 2 输出范围 7 0 5 13#include<bits/stdc.h> #define int long long using namespace std; typedef pair<char,int>PII; const int N2e510;…

应急响应-Windows-挖矿病毒

随着虚拟货币市场的繁荣&#xff0c;挖矿病毒已成为网络安全领域一大挑战。该类病毒利用计算机资源进行加密货币的挖掘&#xff0c;给个人用户和企业网络带来了严重的安全风险。本文将针对挖矿病毒的应急响应和防范措施进行分析和总结。 一.判断挖矿病毒 服务器突然发现CPU资…

树莓派发送指令控制FPGA板子上的流水灯程序

文章目录 前言一、树莓派简介二、整体实现步骤三、树莓派设置四、树莓派串口代码五、Verilog代码5.1 串口接收模块5.2 流水灯模块 六、quartus引脚绑定七、 运行效果总结参考 前言 ​ 本次实验的目的是通过树莓派和FPGA之间的串口通信&#xff0c;控制FPGA开发板上的小灯。实验…

OpenAI 发布新款大型语言模型 GPT-4o,带大家了解最新ChatGPT动态。

OpenAI 发布新款大型语言模型 GPT-4o 昨日OpenAI 举办了一场线上活动&#xff0c;正式发布了其最新研发的 AI 模型 GPT-4o&#xff0c;并详细介绍了该模型的强大功能和未来发展规划。此次发布标志着 AI 技术的重大突破&#xff0c;为用户提供了更加便捷、高效的 AI 工具&#…

css多种布局方式

css多种布局方式 简介标准流布局&#xff08;主要依赖margin\padding&#xff09;浮动布局&#xff08;float&#xff09;定位布局&#xff08;position&#xff09;弹性布局&#xff08;flex&#xff09;网格布局&#xff08;grid&#xff09;多列布局&#xff08;column&…

【论文阅读】<YOLOP: You Only Look Once for PanopticDriving Perception>

Abstract 全视驾驶感知系统是自动驾驶的重要组成部分。一个高精度的实时感知系统可以帮助车辆在驾驶时做出合理的决策。我们提出了一个全视驾驶感知网络&#xff08;您只需寻找一次全视驾驶感知网络&#xff08;YOLOP&#xff09;&#xff09;&#xff0c;以同时执行交通目标检…

7.STL中string的一些超常用函数 (附习题)

目录 1.find 2.atoi 3.to_string 4.getline 【leetcode 习题】 387.字符串中的第一个唯一字符 125. 验证回文串 1.find 1.查找第一次出现的目标字符串&#xff1a;说明&#xff1a;如果查找成功则输出查找到的第一个位置&#xff0c;否则返回-1&#xff1b; s1.find(s2…

嗨动PDF编辑器怎么删除pdf空白页?教程来啦!

嗨动PDF编辑器怎么删除pdf空白页&#xff1f;在日常的工作和学习中&#xff0c;PDF文件因其跨平台兼容性和稳定性而备受青睐。然而&#xff0c;在编辑PDF文件时&#xff0c;我们往往会遇到一些令人头疼的问题&#xff0c;其中最常见的就是空白页。空白页不仅浪费了宝贵的存储空…

ESP32-S3+86盒线控器方案,含开发时问题技术解答

随着智能家居产品越来越多&#xff0c;线控器应用也加大&#xff0c;86盒线控器跟智能吹风机联动&#xff0c;跟中央空调联动&#xff0c;下面讲下ESP32-S386盒线控器方案在开发中遇到的问题。 一、ESP32-S386盒线控器方案&#xff1a; 1、无需网关&#xff0c;可以直接连家里…

零基础10 天入门 Web3之第3天

10 天入门 Web3之第3天 什么是以太坊&#xff0c;以太坊能做什么&#xff1f;Web3 是互联网的下一代&#xff0c;它将使人们拥有自己的数据并控制自己的在线体验。Web3 基于区块链技术&#xff0c;该技术为安全、透明和可信的交易提供支持。我准备做一个 10 天的学习计划&…

Linux系统运行级别

Linux系统运行级别&#xff1a;linux系统共有7个运行级别&#xff0c;不同的级别运行的程序和功能都是不一样的而linux系统默认是运行在一个标准级别上&#xff0c;系统运行级别文件/etc/inittab 运行级别0&#xff1a;所有进程被终止&#xff0c;机器将有序的停止&#xff0c;…

SpringBootWeb 篇-深入了解请求响应(服务端接收不同类型的请求参数的方式)

&#x1f525;博客主页&#xff1a; 【小扳_-CSDN博客】 ❤感谢大家点赞&#x1f44d;收藏⭐评论✍ 文章目录 1.0 请求响应概述 1.1 简单参数 1.2 实体参数 2.3 数组集合参数 2.4 日期参数 2.5 json 参数 2.6 路径参数 3.0 完整代码 1.0 请求响应概述 当客户端发送不同的请求参…

【专利】一种日志快速分析方法、设备、存储介质

公开号CN116560938A申请号CN202310311478.5申请日2023.03.28 是我在超音速人工智能科技股份有限公司(833753) 职务作品&#xff0c;第一发明人是董事长夫妇&#xff0c;第二发明人是我。 ** 注意** &#xff1a; 内容比较多&#xff0c;还有流程图、界面等。请到 专利指定页面…

30-10y国债利差反转的必要条件

国君宏观表示&#xff0c;30-10y期限利差定价本质上是私人部门主动加杠杆的预期&#xff0c;央行购债与资本市场的正向反馈是可以期待的一种场景&#xff0c;而企业部门的被动加杠杆须以可持续的需求动能与盈利预期作为前提。 要点&#xff1a; 期限利差的本质&#xff1a;对长…

如何压缩图片大小?7个实用软件教你快速压缩图片大小

如何压缩图片大小&#xff1f;7个实用软件教你快速压缩图片大小 以下是七个实用的软件&#xff0c;可以帮助您快速压缩图片大小&#xff1a; 图片编辑助手&#xff1a;这是一款功能强大的图像处理软件&#xff0c;其中包含了图像压缩功能。您可以打开需要压缩的图片&#xf…

手撕C语言题典——环形链表的约瑟夫问题

目录 前言 一.故事背景 二.题目 ​编辑三.思路 1&#xff09;数组 ​编辑2&#xff09; 循环链表 四.代码实现 搭配食用更佳哦~~ 数据结构之单单单——链表-CSDN博客 数据结构之单链表的基本操作-CSDN博客 前面学了单链表的相关知识&#xff0c;我们来尝试做一下关于…