挑战一个月基本掌握C++(第十二天)了解命名空间,模板,预处理器
一 命名空间
假设这样一种情况,当一个班上有两个名叫 Zara 的学生时,为了明确区分它们,我们在使用名字之外,不得不使用一些额外的信息,比如他们的家庭住址,或者他们父母的名字等等。
同样的情况也出现在 C++ 应用程序中。例如,您可能会写一个名为 xyz() 的函数,在另一个可用的库中也存在一个相同的函数 xyz()。这样,编译器就无法判断您所使用的是哪一个 xyz() 函数。
因此,引入了命名空间这个概念,专门用于解决上面的问题,它可作为附加信息来区分不同库中相同名称的函数、类、变量等。使用了命名空间即定义了上下文。本质上,命名空间就是定义了一个范围。
我们举一个计算机系统中的例子,一个文件夹(目录)中可以包含多个文件夹,每个文件夹中不能有相同的文件名,但不同文件夹中的文件可以重名。
1.1 定义命名空间
命名空间的定义使用关键字 namespace,后跟命名空间的名称,如下所示:
namespace namespace_name {// 代码声明
}
为了调用带有命名空间的函数或变量,需要在前面加上命名空间的名称,如下所示:
name::code; // code 可以是变量或函数
命名空间如何为变量或函数等实体定义范围:
#include <iostream>
using namespace std;// 第一个命名空间
namespace first_space{void func(){cout << "Inside first_space" << endl;}
}
// 第二个命名空间
namespace second_space{void func(){cout << "Inside second_space" << endl;}
}
int main ()
{// 调用第一个命名空间中的函数first_space::func();// 调用第二个命名空间中的函数second_space::func(); return 0;
}
输出:
Inside first_space
Inside second_space
1.2 using 指令
您可以使用 using namespace 指令,这样在使用命名空间时就可以不用在前面加上命名空间的名称。这个指令会告诉编译器,后续的代码将使用指定的命名空间中的名称。
#include <iostream>
using namespace std;// 第一个命名空间
namespace first_space{void func(){cout << "Inside first_space" << endl;}
}
// 第二个命名空间
namespace second_space{void func(){cout << "Inside second_space" << endl;}
}
using namespace first_space;
int main ()
{// 调用第一个命名空间中的函数func();return 0;
}
输出:
Inside first_space
using 指令也可以用来指定命名空间中的特定项目。例如,如果您只打算使用 std 命名空间中的 cout 部分:
using std::cout;
随后的代码中,在使用 cout 时就可以不用加上命名空间名称作为前缀,但是 std 命名空间中的其他项目仍然需要加上命名空间名称作为前缀,如下所示:
#include <iostream>
using std::cout;int main ()
{cout << "std::endl is used with std!" << std::endl;return 0;
}
输出:
std::endl is used with std!
using 指令引入的名称遵循正常的范围规则。名称从使用 using 指令开始是可见的,直到该范围结束。此时,在范围以外定义的同名实体是隐藏的。
1.3 不连续的命名空间
命名空间可以定义在几个不同的部分中,因此命名空间是由几个单独定义的部分组成的。一个命名空间的各个组成部分可以分散在多个文件中。
所以,如果命名空间中的某个组成部分需要请求定义在另一个文件中的名称,则仍然需要声明该名称。下面的命名空间定义可以是定义一个新的命名空间,也可以是为已有的命名空间增加新的元素:
namespace namespace_name {// 代码声明
}
1.4 嵌套的命名空间
命名空间可以嵌套,您可以在一个命名空间中定义另一个命名空间,如下所示:
namespace namespace_name1 {// 代码声明namespace namespace_name2 {// 代码声明}
}
可以通过使用 :: 运算符来访问嵌套的命名空间中的成员:
// 访问 namespace_name2 中的成员
using namespace namespace_name1::namespace_name2;// 访问 namespace_name1 中的成员
using namespace namespace_name1;
在上面的语句中,如果使用的是 namespace_name1,那么在该范围内 namespace_name2 中的元素也是可用的,如下所示:
#include <iostream>
using namespace std;// 第一个命名空间
namespace first_space{void func(){cout << "Inside first_space" << endl;}// 第二个命名空间namespace second_space{void func(){cout << "Inside second_space" << endl;}}
}
using namespace first_space::second_space;
int main ()
{// 调用第二个命名空间中的函数func();return 0;
}
输出:
Inside second_space
二 模板
模板是泛型编程的基础,泛型编程即以一种独立于任何特定类型的方式编写代码。
模板是创建泛型类或函数的蓝图或公式。库容器,比如迭代器和算法,都是泛型编程的例子,它们都使用了模板的概念。
每个容器都有一个单一的定义,比如 向量,我们可以定义许多不同类型的向量,比如 vector <int> 或 vector <string>。
可以使用模板来定义函数和类,接下来让我们一起来看看如何使用。
2.1 函数模板
模板函数定义的一般形式如下所示:
template <typename type> ret-type func-name(parameter list)
{// 函数的主体
}
在这里,type 是函数所使用的数据类型的占位符名称。这个名称可以在函数定义中使用。
下面是函数模板的实例,返回两个数中的最大值:
#include <iostream>
#include <string>using namespace std;template <typename T>
inline T const& Max (T const& a, T const& b)
{ return a < b ? b:a;
}
int main ()
{int i = 39;int j = 20;cout << "Max(i, j): " << Max(i, j) << endl; double f1 = 13.5; double f2 = 20.7; cout << "Max(f1, f2): " << Max(f1, f2) << endl; string s1 = "Hello"; string s2 = "World"; cout << "Max(s1, s2): " << Max(s1, s2) << endl; return 0;
}
输出:
Max(i, j): 39
Max(f1, f2): 20.7
Max(s1, s2): World
2.2 类模板
正如我们定义函数模板一样,我们也可以定义类模板。泛型类声明的一般形式如下所示:
template <class type> class class-name {
.
.
.
}
在这里,type 是占位符类型名称,可以在类被实例化的时候进行指定。您可以使用一个逗号分隔的列表来定义多个泛型数据类型。
下面的实例定义了类 Stack<>,并实现了泛型方法来对元素进行入栈出栈操作:
#include <iostream>
#include <vector>
#include <cstdlib>
#include <string>
#include <stdexcept>using namespace std;template <class T>
class Stack { private: vector<T> elems; // 元素 public: void push(T const&); // 入栈void pop(); // 出栈T top() const; // 返回栈顶元素bool empty() const{ // 如果为空则返回真。return elems.empty(); }
}; template <class T>
void Stack<T>::push (T const& elem)
{ // 追加传入元素的副本elems.push_back(elem);
} template <class T>
void Stack<T>::pop ()
{ if (elems.empty()) { throw out_of_range("Stack<>::pop(): empty stack"); }// 删除最后一个元素elems.pop_back();
} template <class T>
T Stack<T>::top () const
{ if (elems.empty()) { throw out_of_range("Stack<>::top(): empty stack"); }// 返回最后一个元素的副本 return elems.back();
} int main()
{ try { Stack<int> intStack; // int 类型的栈 Stack<string> stringStack; // string 类型的栈 // 操作 int 类型的栈 intStack.push(7); cout << intStack.top() <<endl; // 操作 string 类型的栈 stringStack.push("hello"); cout << stringStack.top() << std::endl; stringStack.pop(); stringStack.pop(); } catch (exception const& ex) { cerr << "Exception: " << ex.what() <<endl; return -1;}
}
输出:
7
hello
Exception: Stack<>::pop(): empty stack
三 预处理器
预处理器是一些指令,指示编译器在实际编译之前所需完成的预处理。
所有的预处理器指令都是以井号(#)开头,只有空格字符可以出现在预处理指令之前。预处理指令不是 C++ 语句,所以它们不会以分号(;)结尾。
我们已经看到,之前所有的实例中都有 #include 指令。这个宏用于把头文件包含到源文件中。
C++ 还支持很多预处理指令,比如 #include、#define、#if、#else、#line 等,让我们一起看看这些重要指令。
3.1 #define 预处理
#define 预处理指令用于创建符号常量。该符号常量通常称为宏,指令的一般形式是:
#define macro-name replacement-text
当这一行代码出现在一个文件中时,在该文件中后续出现的所有宏都将会在程序编译之前被替换为 replacement-text。例如:
#include <iostream>
using namespace std;#define PI 3.14159int main ()
{cout << "Value of PI :" << PI << endl; return 0;
}
现在,让我们测试这段代码,看看预处理的结果。假设源代码文件已经存在,接下来使用 -E 选项进行编译,并把结果重定向到 test.p。现在,如果您查看 test.p 文件,将会看到它已经包含大量的信息,而且在文件底部的值被改为如下:
$ gcc -E test.cpp > test.p...
int main ()
{cout << "Value of PI :" << 3.14159 << endl; return 0;
}
3.2 参数宏
可以使用 #define 来定义一个带有参数的宏,如下所示:
#include <iostream>
using namespace std;#define MIN(a,b) (a<b ? a : b)int main ()
{int i, j;i = 100;j = 30;cout <<"较小的值为:" << MIN(i, j) << endl;return 0;
}
输出:
较小的值为:30
3.3 条件编译
有几个指令可以用来有选择地对部分程序源代码进行编译。这个过程被称为条件编译。
条件预处理器的结构与 if 选择结构很像。请看下面这段预处理器的代码:
#ifdef NULL#define NULL 0
#endif
可以只在调试时进行编译,调试开关可以使用一个宏来实现,如下所示:
#ifdef DEBUGcerr <<"Variable x = " << x << endl;
#endif
如果在指令 #ifdef DEBUG 之前已经定义了符号常量 DEBUG,则会对程序中的 cerr 语句进行编译。您可以使用 #if 0 语句注释掉程序的一部分,如下所示:
#if 0不进行编译的代码
#endif
实例:
#include <iostream>
using namespace std;
#define DEBUG#define MIN(a,b) (((a)<(b)) ? a : b)int main ()
{int i, j;i = 100;j = 30;
#ifdef DEBUGcerr <<"Trace: Inside main function" << endl;
#endif#if 0/* 这是注释部分 */cout << MKSTR(HELLO C++) << endl;
#endifcout <<"The minimum is " << MIN(i, j) << endl;#ifdef DEBUGcerr <<"Trace: Coming out of main function" << endl;
#endifreturn 0;
}
输出:
Trace: Inside main function
The minimum is 30
Trace: Coming out of main function
3.4 # 和 ## 运算符
# 和 ## 预处理运算符在 C++ 和 ANSI/ISO C 中都是可用的。# 运算符会把 replacement-text 令牌转换为用引号引起来的字符串。
请看下面的宏定义:
#include <iostream>
using namespace std;#define MKSTR( x ) #xint main ()
{cout << MKSTR(HELLO C++) << endl;return 0;
}
输出:
HELLO C++
让我们来看看它是如何工作的。不难理解,C++ 预处理器把下面这行:
cout << MKSTR(HELLO C++) << endl;
转换成了:
cout << "HELLO C++" << endl;
## 运算符用于连接两个令牌。下面是一个实例:
#define CONCAT( x, y ) x ## y
当 CONCAT 出现在程序中时,它的参数会被连接起来,并用来取代宏。例如,程序中 CONCAT(HELLO, C++) 会被替换为 "HELLO C++",如下面实例所示。
#include <iostream>
using namespace std;#define concat(a, b) a ## b
int main()
{int xy = 100;cout << concat(x, y);return 0;
}
输出:
100
让我们来看看它是如何工作的。不难理解,C++ 预处理器把下面这行:
cout << concat(x, y);
转换成了:
cout << xy;
3.5 C++中的预定义宏
宏 | 描述 |
---|---|
__LINE__ | 这会在程序编译时包含当前行号。 |
__FILE__ | 这会在程序编译时包含当前文件名。 |
__DATE__ | 这会包含一个形式为 month/day/year 的字符串,它表示把源文件转换为目标代码的日期。 |
__TIME__ | 这会包含一个形式为 hour:minute:second 的字符串,它表示程序被编译的时间。 |
实例:
#include <iostream>
using namespace std;int main ()
{cout << "Value of __LINE__ : " << __LINE__ << endl;cout << "Value of __FILE__ : " << __FILE__ << endl;cout << "Value of __DATE__ : " << __DATE__ << endl;cout << "Value of __TIME__ : " << __TIME__ << endl;return 0;
}
输出:
Value of __LINE__ : 6
Value of __FILE__ : test.cpp
Value of __DATE__ : Feb 28 2011
Value of __TIME__ : 18:52:48
相关文章:

挑战一个月基本掌握C++(第十二天)了解命名空间,模板,预处理器
一 命名空间 假设这样一种情况,当一个班上有两个名叫 Zara 的学生时,为了明确区分它们,我们在使用名字之外,不得不使用一些额外的信息,比如他们的家庭住址,或者他们父母的名字等等。 同样的情况也出现在 …...
python实现根据搜索关键词爬取某宝商品信息
当程序打开淘宝登陆页面后,需要快速手动登录淘宝,如果服务报错,需要重新登录! pip安装库 pip install pyquery pip install selenium pip install openpyxl # 代码说明:代码功能: 基于ChromeDriver爬取tao…...
Posison Distribution
泊松分布 (Poisson Distribution) 泊松分布是概率论中的一个重要离散分布,描述单位时间或单位空间内随机事件发生的次数,假设事件是独立的且平均发生率是已知的。 定义 泊松分布的概率质量函数 (PMF) 为: P ( X k ) λ k e − λ k ! , …...

2024年最新多目标优化算法:多目标麋鹿群优化算法(MOEHO)求解ZDT1-ZDT4,ZDT6及工程应用---盘式制动器设计,提供完整MATLAB代码
一、麋鹿群优化算法 麋鹿群优化算法(Elephant Herding Optimization,EHO)是2024年提出的一种启发式优化算法,它的灵感来自麋鹿群的繁殖过程。麋鹿有两个主要的繁殖季节:发情和产犊。在发情季节,麋鹿群分裂…...

使用Webpack构建微前端应用
英文社区对 Webpack Module Federation 的响应非常热烈,甚至被誉为“A game-changer in JavaScript architecture”,相对而言国内对此热度并不高,这一方面是因为 MF 强依赖于 Webpack5,升级成本有点高;另一方面是国内已…...

Apache RocketMQ 5.1.3安装部署文档
官方文档不好使,可以说是一坨… 关键词:Apache RocketMQ 5.0 JDK 17 废话少说,开整。 1.版本 官网地址,版本如下。 https://rocketmq.apache.org/download2.配置文件 2.1namesrv端口 在ROCKETMQ_HOME/conf下 新增namesrv.pro…...
CMS(Concurrent Mark Sweep)垃圾回收器的具体流程
引言 CMS(Concurrent Mark Sweep)收集器是Java虚拟机中的一款并发收集器,其设计目标是最小化停顿时间,非常适合于对响应时间敏感的应用。与传统的串行或并行收集器不同,CMS能够尽可能地让垃圾收集线程与用户线程同时运…...

【Linux】Socket编程-UDP构建自己的C++服务器
🌈 个人主页:Zfox_ 🔥 系列专栏:Linux 目录 一:🔥 UDP 网络编程 🦋 接口讲解🦋 V1 版本 - echo server🦋 V2 版本 - DictServer🦋 V3 版本 - 简单聊天室 二&a…...

磁盘结构、访问时间、调度算法
目录 一、什么是磁盘? 二、磁盘分类 1、从磁头分 2、通过盘面分 三、一次磁盘读/写的时间 四、磁盘调度算法 1、先来先到服务算法FCFS 2、最短寻找时间优先SSTF 3、扫描算法(SCAN) 4、LOOk算法 5、循环扫描算法(C-SCAN…...
详解归并排序
归并排序 归并排序的基本概念归并排序的详细步骤1. 分解阶段2. 合并阶段3. 归并排序的递归流程 时间复杂度分析空间复杂度分析算法步骤2-路归并排序代码分析代码讲解1. 合并两个子数组的函数 merge()2. 归并排序函数 mergeSort()3. 打印数组的函数 printArray()4. 主函数 main(…...

45.在 Vue 3 中使用 OpenLayers 鼠标点击播放视频
引言 在 Web 开发中,地图可视化和互动功能是越来越重要的应用场景。OpenLayers 是一个强大的开源 JavaScript 库,用于显示和处理地图数据,支持多种地图服务和交互功能。在这个教程中,我们将介绍如何在 Vue 3 中集成 OpenLayers&a…...

《大话Java+playWright》系列教程初级篇-初识
后续代码会整理开源-大家期待吧!!! 首先讲下为啥不用python,因为不想下载各种安装插件,太麻烦了,好多不兼容。 所以选择了java。 先来讲下什么是playwright,playwright是微软开源自动化测试工…...

05.HTTPS的实现原理-HTTPS的握手流程(TLS1.2)
05.HTTPS的实现原理-HTTPS的握手流程(TLS1.2) 简介1. TLS握手过程概述2. TLS握手过程细化3. 主密钥(对称密钥)生成过程4. 密码规范变更 简介 主要讲述了混合加密流程完成后,客户端和服务器如何共同获得相同的对称密钥…...
提示词工程
一、六何分析法快速写出准确的提示词 英文单词中文解释提问时的思考示例Why何故问题的背景,包括为什么做及目标(做成什么样)最近我们要与某品牌合作推广冲牙器,对方需要我们策划一场营销活动What何事具体是什么事写一个营销策划方…...
基于python网络爬虫的搜索引擎设计
一、毕业设计(论文)题目:基于网络爬虫的搜索引擎设计 - 基于网络爬虫的搜索引擎设计1 二、毕业设计(论文)工作自 2022-09-01 起至 2022-10-28 止 三、毕业设计(论文)内容要求: 主…...

ip-协议
文章目录 1. 网络层2. ip协议2.1 ip协议格式2.2 网段划分基本概念网段划分的两种方式为什么要网段划分?特殊的IP地址IP地址数量不足 2.3 私有IP与公网IP2.4 路由 3. IP的分片与组装为什么要分片与组装?如何分片?如何组装? 1. 网络…...

Git(11)之log显示支持中文
Git(11)之log显示支持中文 Author:Once Day Date:2024年12月21日 漫漫长路有人对你微笑过嘛… 参考文档:GIT使用log命令显示中文乱码_gitlab的log在matlab里显示中文乱码-CSDN博客 全系列文章可查看专栏: Git使用记录_Once_day的博客-CSD…...

oneflow深度学习框架使用问题总结(Windows/Linux)
目录 1.简述 2.在Windows下使用Oneflow深度学习框架(错误记录,谨慎,官方不支持,需要WSL) 2.1安装Anaconda 2.1创建虚拟环境 2.2安装Pytorch 2.3安装Pycharm 2.4 安装Oneflow 3.在Linux下使用Oneflow深度学习框…...

论文研读:AnimateDiff—通过微调SD,用图片生成动画
1.概述 AnimateDiff 设计了3个模块来微调通用的文生图Stable Diffusion预训练模型, 以较低的消耗实现图片到动画生成。 论文名:AnimateDiff: Animate Your Personalized Text-to-Image Diffusion Models without Specific Tuning 三大模块: 视频域适应…...
SQLAlchemy示例(连接数据库插入表数据)
背景需求 连接数据库,插入表中一些数据。 其用户是新建用户,所以只能插入,不能更新。 再次输入数据则使用更新数据语法,这个没调试。 #! /usr/bin/env python # -*- coding: utf-8 -*-from sqlalchemy import create_engine, …...

UE5 学习系列(二)用户操作界面及介绍
这篇博客是 UE5 学习系列博客的第二篇,在第一篇的基础上展开这篇内容。博客参考的 B 站视频资料和第一篇的链接如下: 【Note】:如果你已经完成安装等操作,可以只执行第一篇博客中 2. 新建一个空白游戏项目 章节操作,重…...

eNSP-Cloud(实现本地电脑与eNSP内设备之间通信)
说明: 想象一下,你正在用eNSP搭建一个虚拟的网络世界,里面有虚拟的路由器、交换机、电脑(PC)等等。这些设备都在你的电脑里面“运行”,它们之间可以互相通信,就像一个封闭的小王国。 但是&#…...
【git】把本地更改提交远程新分支feature_g
创建并切换新分支 git checkout -b feature_g 添加并提交更改 git add . git commit -m “实现图片上传功能” 推送到远程 git push -u origin feature_g...
Redis的发布订阅模式与专业的 MQ(如 Kafka, RabbitMQ)相比,优缺点是什么?适用于哪些场景?
Redis 的发布订阅(Pub/Sub)模式与专业的 MQ(Message Queue)如 Kafka、RabbitMQ 进行比较,核心的权衡点在于:简单与速度 vs. 可靠与功能。 下面我们详细展开对比。 Redis Pub/Sub 的核心特点 它是一个发后…...

用机器学习破解新能源领域的“弃风”难题
音乐发烧友深有体会,玩音乐的本质就是玩电网。火电声音偏暖,水电偏冷,风电偏空旷。至于太阳能发的电,则略显朦胧和单薄。 不知你是否有感觉,近两年家里的音响声音越来越冷,听起来越来越单薄? —…...

NXP S32K146 T-Box 携手 SD NAND(贴片式TF卡):驱动汽车智能革新的黄金组合
在汽车智能化的汹涌浪潮中,车辆不再仅仅是传统的交通工具,而是逐步演变为高度智能的移动终端。这一转变的核心支撑,来自于车内关键技术的深度融合与协同创新。车载远程信息处理盒(T-Box)方案:NXP S32K146 与…...

Mysql中select查询语句的执行过程
目录 1、介绍 1.1、组件介绍 1.2、Sql执行顺序 2、执行流程 2.1. 连接与认证 2.2. 查询缓存 2.3. 语法解析(Parser) 2.4、执行sql 1. 预处理(Preprocessor) 2. 查询优化器(Optimizer) 3. 执行器…...

[大语言模型]在个人电脑上部署ollama 并进行管理,最后配置AI程序开发助手.
ollama官网: 下载 https://ollama.com/ 安装 查看可以使用的模型 https://ollama.com/search 例如 https://ollama.com/library/deepseek-r1/tags # deepseek-r1:7bollama pull deepseek-r1:7b改token数量为409622 16384 ollama命令说明 ollama serve #:…...
6个月Python学习计划 Day 16 - 面向对象编程(OOP)基础
第三周 Day 3 🎯 今日目标 理解类(class)和对象(object)的关系学会定义类的属性、方法和构造函数(init)掌握对象的创建与使用初识封装、继承和多态的基本概念(预告) &a…...
计算机系统结构复习-名词解释2
1.定向:在某条指令产生计算结果之前,其他指令并不真正立即需要该计算结果,如果能够将该计算结果从其产生的地方直接送到其他指令中需要它的地方,那么就可以避免停顿。 2.多级存储层次:由若干个采用不同实现技术的存储…...