【C++程序员的自我修炼】基础语法篇(一)

心中若有桃花源

何处不是水云间


目录

命名空间

💞命名空间的定义

💞 命名空间的使用

输入输出流

 缺省参数

函数的引用

引用的定义💞

引用的表示💞

 引用的特性💞

 常量引用💞

引用的使用场景 

做参数

做返回值

 ⭐传值、传引用效率比较

 ⭐引用和指针的区别

命名空间

✨在C/C++中,变量、函数和后面要学到的类都是大量存在的,这些变量、函数和类的名称将都存在于全局作用域中,可能会导致很多冲突。

举个栗子:我想输出全局变量 rand 的值

#include<stdlib.h>
#include<stdio.h>int rand = 1;int main()
{printf("%d\n", rand);
}

我们发现 rand 重定义了,因为 #include<stdio.h> 内有以 rand 命名的函数,如果是以前我们为了解决这个问题还要憋屈的去改变量名,而现在 C++ 中的 命名空间 就可以很好的解决问题。

使用命名空间的目的对标识符的名称进行本地化 以避免命名冲突或名字污染

💞命名空间的定义

namespace是命名空间的关键字,Df是命名空间的名字,一个命名空间就定义了一个新的作用域,命名空间中的所有内容都局限于该命名空间中
namespace Df
{int rand = 10;
}
⭐命名空间可以嵌套(函数、结构体、另一个命名空间等)
namespace N1
{int a;int b;int Add(int left, int right){return left + right;}namespace N2{int c;int d;int Sub(int left, int right){return left - right;}}
}
⭐同一个工程中允许存在多个相同名称的命名空间,编译器最后会合成同一个命名空间中
namespace N1
{int a;int b;int Add(int left, int right){return left + right;}namespace N2{int c;int d;int Sub(int left, int right){return left - right;}}
}namespace N1
{int Mul(int left, int right){return left * right;}
}

💞 命名空间的使用

✨<1>加命名空间名称及作用域限定符,:: 是域限定符表示在该命名空间进行访问
namespace Df
{int a = 0;
}
int main()
{printf("a = %d\n",Df::a);system("pause");
}

✨<2>使用 using 将命名空间中某个成员引入
namespace Df
{int b = 1;
}
using Df::b;
int main()
{printf("b = %d\n", b);system("pause");
}

✨<3>使用 using namespace 命名空间名称对该空间进行展开
namespace Df
{int a = 0;int b = 1;
}
using namespace Df;
int main()
{printf("a = %d\n", a);printf("b = %d\n", b);system("pause");
}

 日常操作🔥

c++库为了防止命名冲突,就把库里面的东西都定义在一个 std 的命名空间里,所以要使用c++库中的函数方法与上述三种方法一样

<1>加命名空间名称及作用域限定符

#include<iostream>
int main()
{std::cout << "Hello world !" << std::endl;return 0;
}

 <2>使用 using 将命名空间中某个成员引入

using std::cout;
using std::endl;
cout << "Hello world !" << endl;

 <3>对全部命名空间进行展

using namespace std;
cout << "Hello world !" << endl;

一些小细节💥

<1>编译默认查找顺序:当前局部域、全局域、命名空间中查找

举个栗子

如果把对变量的查找比作在地里采菜,那么命名空间就是一个访问权限问题

局部域自己家的地
全局域外面的野地
命名空间隔壁王叔叔家的地
假如某天家里缺辣椒了,自家地里没有,那就要去外面的野地去查找,若还是没有就看看隔壁王叔叔家的地有没有(如果有也不能随便采摘,因为没有经过王叔叔的同意),有权限后才可以采摘(域限定符进行申请访问)。

<2>对全部命名空间进行展开 using namespace std (大型项目不宜使用,避免命名冲突)


输入输出流

cout 标准输出流 和 cin 标准输入流,包含于 #include<iostream> 头文件中

🌤️ >> 是流插入与 cin 搭配使用,<<是流输出与 cout 搭配使用
🌤️ 输入输出流可以自动识别变量类型不需要像 printf/scanf 输入输出时那样,需要手动控制格式
🌤️ cout cin 是全局的流对象,endl 表示换行输出
#include<iostream>
using std::cout;
using std::cin;
using std::endl;
int main()
{int a;double b;cin >> a >> b;cout << a << b << endl;return 0;
}


 缺省参数

缺省参数是声明或定义函数时为函数的参数指定一个缺省值

在调用该函数时,如果没有指定实参则采用该形参的缺省值,否则使用指定的实参

#include<iostream>
using std::cout;
using std::endl;
void Fun(int a = 10)
{cout << a << endl;
}int main(void)
{Fun();//没有指定实参则采用缺省值Fun(314);return  0;
}

缺省参数的类型

🌤️全缺省:形参都是表达式
#include<iostream>
using std::cout;
using std::endl;
void Fun(int a = 10,int b = 20,int c = 30)
{cout << a << endl;cout << b << endl;cout << c << endl;
}
int main(void)
{Fun(1);return  0;
}

注意:

<1>缺省参数不是要全部传完,没有指定实参采用该形参的缺省值

<2>缺省值必须从左往右依次传,不可以间隔

🌤️半缺省:形参部分没有表达式
#include<iostream>
using std::cout;
using std::endl;
void Fun(int a,int b = 20,int c = 30)
{cout << a << endl;cout << b << endl;cout << c << endl;
}
int main(void)
{Fun(1);return  0;
}

注意:

<1>半缺省参数必须从右往左依次来给出,不能间隔着给

void Fun(int a = 10,int b = 20,int c)
{cout << a << endl;cout << b << endl;cout << c << endl;
}
int main(void)
{Fun(1);return  0;
}


因为第三个形参 c 不是表达式,没有缺省值,而我们实参传的数值是传给 a 的所以报错

<2>缺省参数不能在函数声明和定义中同时出现

#include<iostream>
using std::cout;
using std::endl;
void Fun(int a = 10, int b = 20, int c = 30);int main()
{Fun(1);return  0;
}void Fun(int a = 10, int b = 20, int c = 30)
{cout << a << endl;cout << b << endl;cout << c << endl;
}


函数的引用

引用的定义💞

引用不是新定义一个变量,而是给已存在变量取了一个别名

编译器不会为引用变量开辟内存空 间,它和它引用的变量共用同一块内存空间

引用的表示💞

🌤️类型& 引用变量名(对象名) = 引用实体
#include<iostream>
using std::cout;
using std::endl;
int main()
{int a = 10;int& b = a;cout << "a = " << a << endl;cout << "b = " << b << endl;cout << "&a = " << &a << endl;cout << "&b = " << &b << endl;return  0;
}

 因为 b 是给 a 取的别名,我们可以看到 ab以及地址都是一样的

比如水浒传的李逵,你叫黑旋风和铁牛他都会应,这就是引用(取别名)

注意💥

引用类型必须和引用实体是同种类型的

 引用的特性💞

<1>引用在定义时必须初始化

<2>一个变量可以有多个引用

#include<iostream>
using std::cout;
using std::endl;
int main()
{int a = 10;int& b = a;int& c = b;cout << a << endl << b << endl << c;return  0;
}

<3>引用一旦引用一个实体,再不能引用其他实体

 

🌤️引用在定义时必须初始化
🌤️一个变量可以有多个引用
🌤️引用一旦引用一个实体,再不能引用其他实体

 常量引用💞

	const int a = 10;//int& ra = a;          //该语句编译时会出错,a为常量const int& ra = a;double b = 3.14;//int& rb = b;          //该语句编译时会出错,类型不同double& rb = b;int c = 10;const int& rc = c;   

加了 const 该变量是不能修改的,即成了一种常量,可以理解为只读型

而没加 const 的可理解为可读可写型

const int a = 10;
int& ra = a;

对常量a进行引用,那么我就可以通过引用ra去修改a的值,权限放大了所以是不行的

int c = 10;

const int& rc = c;  

对变量c进行引用,并将c置为不可修改的常量,权限的缩小所以是可行

const int a = 10;
const int& ra = a;

都是const加以修饰的同类型变量,权限的平移是可行的

总结

<1>权限只能缩小,不能放大,放大就会报错

<2>权限放大和缩小只针对引用和指针

<3>使用引用传参,函数内不改变参数,尽量使用 const 引用传参

引用的使用场景 

做参数

💞<1>还记得我们之前写过的两数交换的代码吗?我们之前传的是指针,我们可以用引用代替

#include<iostream>
using std::cout;
using std::endl;void Swap(int& n, int& m)
{int tmp = n;n = m;m = tmp;
}
int main()
{int a = 10, b = 20;Swap(a, b);cout << a << endl << b << endl;return  0;
}

 💞<2>还记得我们之前写过链表时传的二级指针吗?今非昔比,我们来干爆他

前期回顾:单链表

typedef int SListDataType;typedef struct SList
{SListDataType data;struct SList* next;
}SL;void SListFront(SL** head, SListDataType x)
{...
}

这是我们的第一种写法,当初为啥要传二级指针呢?

因为我们没有定义哨兵位(头节点)而我们又想改变我们的首节点,假设我们传单指针SL*,SL*是结构体指针类型,这里是形参(形参是实参的拷贝,出了作用域就销毁),而我们要想改变首节点则必须传地址,所以我们要传二级指针

当然这是我们的一种写法,用引用该如何写呢?

void SListFront(SL*& head, SListDataType x)
{...
}

如果你觉得别扭可以将结构体指针的声明放在 typedef 中像这样

typedef int SListDataType;typedef struct SList
{SListDataType data;struct SList* next;
}SL*;void SListFront(SL& head, SListDataType x)
{...
}

这样是不是好看多了!!!

做返回值

错误示范)·

#include<iostream>
using namespace std;
int& Count()
{int n = 10;return n;
}
int main()
{int& ret = Count();cout << "ret = " << ret << endl;cout << "ret = " << ret << endl;return 0;
}

 让我们来分析分析,以上打印的值都是 10 吗?

我们发现我们打印的第二个 ret 竟然是随机值,这是为什么呢?

因为局部变量存储在系统的栈区,出了定义域就会销毁

🌤️第一次打印原值是因为编译器在释放时会进行一次保留

Count 函数并不是直接返回 n 的

因为 Count 函数在调用结束后会销毁它所在的栈帧,连同 n 会一起销毁,所以编译器会先保存 n的值到一个寄存器中,再销毁栈帧,然后返回寄存器的值给 ret

🌤️第二次出现乱码是因为赋值后寄存器的空间被编译器销毁

当我们用上面的代码,返回的是 n 的引用时,这就不安全了。因为返回的是 n 的引用,不会创建临时空间给 n,而是直接返回 n 。 但是返回之后 n 所在的函数栈帧会被销毁,所以连同 n 一起销毁了,但是此时 ret n 这块已经不属于自己的空间的拷贝,所以 ret 是违法

那我们想二次调用 n 该怎么办呢?只要不销毁 n 就可以了,我们可以给 n 开辟静态空间:

正确写法

#include<iostream>
using namespace std;
int& Count()
{static int n = 10;return n;
}
int main()
{int& ret = Count();cout << "ret = " << ret << endl;cout << "ret = " << ret << endl;return 0;
}

 

此时 n 是被 static 修饰过的变量,可以用引用进行返回了,因为 n 是在静态区开辟的空间在内存的堆区,而函数是在栈区开辟的空间,所以不会被销毁。其次因为在堆区返回的时候就不需要借助寄存器的临时拷贝了。

局部变量用 static 修饰,出作用域后不销毁,可以传引用返回

没有用 static 修饰的局部变量,出了作用域会被销毁栈,必须用传值返回

引用作为函数的返回值时,必须在定义函数时在函数名前&

用引用作函数的返回值的最大的好处是在内存中不产生返回值的副本(寄存器)

我们在举一个栗子~

#include<iostream>
using namespace std;
int& Add(int a, int b)
{int c = a + b;return c;
}
int main()
{int& ret = Add(1, 2);Add(3, 4);cout << "Add(1, 2) is :" << ret << endl;return 0;
}

这里为什么输出的是 7 呢?

 

就像我们学指针时遇到的野指针,在我们 free 掉不用的空间后没有将指针置为NULL,此时该指针还指向这块空间,但是值却是随机的

 

因为 c 是在栈区在第一次调用Add时,ret 为 3,Add函数的栈桢销毁,在第二次调用时,Add函数的栈桢是相同的,c 的位置覆盖为 7,再次访问 ret 此时就为 7,因此这里使用是不安全的

总结💥

如果函数返回时,出了函数作用域,如果返回对象还在,则可以使用引用返回,如果已经还给系统了,则必须使用传值返回


 ⭐传值、传引用效率比较

#include <time.h>
#include<iostream>
using namespace std;
struct A { int a[10000]; };
A a;
// 值返回
A TestFunc1() { return a; }
// 引用返回
A& TestFunc2() { return a; }
void TestReturnByRefOrValue()
{// 以值作为函数的返回值类型size_t begin1 = clock();for (size_t i = 0; i < 100000; ++i)TestFunc1();size_t end1 = clock();// 以引用作为函数的返回值类型size_t begin2 = clock();for (size_t i = 0; i < 100000; ++i)TestFunc2();size_t end2 = clock();// 计算两个函数运算完成之后的时间cout << "TestFunc1 time:" << end1 - begin1 << endl;cout << "TestFunc2 time:" << end2 - begin2 << endl;
}int main()
{TestReturnByRefOrValue();return 0;
}

 我们发现:引用作为返回值类型大大提高了效率

原因:以值作为参数或者返回值类型,在传参和返回期间,函数不会直接传递实参或者将变量本身直接返回,而是传递实参或者返回变量的一份临时的拷贝,因此用值作为参数或者返回值类型,效率是非常低下的,尤其是当参数或者返回值类型非常大时,效率就更低。

 ⭐引用和指针的区别

引用在语法概念上引用就是一个别名,没有独立空间,指针在底层实现上实际是有空间的

#include<iostream>
using namespace std;
int main()
{int a = 10;//指针存储a的地址int* pa = &a;//b是a的引用int& b = a;return 0;
}

 没有NULL引用,但有NULL指针

#include<iostream>
using namespace std;
int main()
{int* a = NULL;int& b = a;return 0;
}

 

 在sizeof中含义不同:引用结果为引用类型的大小,但指针始终是地址空间所占字节个数

#include<iostream>
using namespace std;
int main()
{double a = 10;double* b = &a;  //指针取地址cout << sizeof(b) << endl;double& c = a;   //引用cout << sizeof(c) << endl;return 0;
}

 引用自加即引用的实体增加1,指针自加即指针向后偏移一个类型的大小

#include<iostream>
using namespace std;
int main()
{double a = 10;double* b = &a; cout << b << endl;b++;cout << b << endl;double& c = a;   cout << c << endl;c++;cout << c << endl;return 0;
}

总结 💥

🌤️引用在语法概念上引用就是一个别名,没有独立空间,指针在底层实现上实际是有空间的
🌤️引用在定义时必须初始化,指针没有要求
🌤️引用在初始化时引用一个实体后,就不能再引用其他实体,而指针可以在任何时候指向任何 一个同类型实体
🌤️没有NULL引用,但有NULL指针
🌤️在sizeof中含义不同:引用结果为引用类型的大小,但指针始终是地址空间所占字节个数
🌤️引用自加即引用的实体增加1,指针自加即指针向后偏移一个类型的大小
🌤️有多级指针,但是没有多级引用
🌤️访问实体方式不同,指针需要显式解引用,引用编译器自己处理
🌤️引用指针使用起来相对更安全

 

先介绍到这里啦~

有不对的地方请指出💞

 

 

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

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

相关文章

Docker进阶:Docker Swarm(集群搭建) —实现容器编排的利器

Docker进阶&#xff1a;Docker Swarm&#xff08;集群搭建&#xff09; —实现容器编排的利器 1、什么是Docker Swarm&#xff1f;2、Docker Swarm 与 Docker Compose的区别3、创建一个Swarm集群&#xff08;1-Manager&#xff0c;2-Worker&#xff09;1、资源准备2、初始化Swa…

使用Windows编辑工具直接编辑Linux上的代码

windows直接编辑Linux上代码 步骤如下&#xff1a; 1、按照samba 2、vim /etc/samba/smb.conf 3、输入以下内容 4 、重启下samba 输入以下内容 pkill smbd smbd 查看采用 ps -ef|grep smbd 给予文件权限&#xff1a; chmod 777 /code 5、winr 修改全选才能进入并且编写。…

快速上手Spring Cloud 六:容器化与微服务化

快速上手Spring Cloud 一&#xff1a;Spring Cloud 简介 快速上手Spring Cloud 二&#xff1a;核心组件解析 快速上手Spring Cloud 三&#xff1a;API网关深入探索与实战应用 快速上手Spring Cloud 四&#xff1a;微服务治理与安全 快速上手Spring Cloud 五&#xff1a;Spring …

vue3+ts白屏问题解决

文章目录 打开白屏解决方法可能出现问题使用base导致的使用baseUrl导致的 注意点vue3ts白屏问题知识分享 打开白屏 解决方法 在vue.config.js页面 添加publicPath:./, const { defineConfig } require(vue/cli-service)module.exports defineConfig({ transpileDependenci…

AcWing 4609:火柴棍数字 ← 贪心算法

【题目来源】 https://www.acwing.com/problem/content/4612/【题目描述】 给定 n 个火柴棍&#xff0c;你可以用它们摆出数字 0∼9。 摆出每个数字所需要的具体火柴棍数量如下图所示&#xff1a; 请你用这些火柴棍摆成若干个数字&#xff0c;并把这些数字排成一排组成一个整数…

Git版本管理使用手册 - 8 - 合并分支、解决冲突

合并整个开发分支 切换到本地test分支&#xff0c;选择右下角远程开发分支&#xff0c;选择Merge into Current。然后提交到远程test仓库。 合并某次提交的代码 当前工作区切换成test分支&#xff0c;选择远程仓库中的dev开发分支&#xff0c;选择需要合并的提交版本右击&a…

SQL Server 实验二:数据库视图的创建和使用

目录 第一关 相关知识 什么是表 操作数据表 创建数据表 插入数据 修改表结构 删除数据表 编程要求 第一关实验代码&#xff1a; 第二关 相关知识 视图是什么 视图的优缺点 视图的优点 视图的缺点 操作视图 创建视图 通过视图向基本表中插入数据 通过视图修改基本表的…

由浅到深认识Java语言(37):I/O流

该文章Github地址&#xff1a;https://github.com/AntonyCheng/java-notes 在此介绍一下作者开源的SpringBoot项目初始化模板&#xff08;Github仓库地址&#xff1a;https://github.com/AntonyCheng/spring-boot-init-template & CSDN文章地址&#xff1a;https://blog.c…

win10配置CLion2022+ubuntu20.04远程部署

背景 在博文ubunut搭建aarch64 cuda交叉编译环境记录中&#xff0c;使用的ubuntu20.04虚拟机安装eclipse来交叉编译aarch64的程序&#xff0c;然后发送到jetson板子上执行。开发一段时间后发现eclipse IDE使用起来不太便捷&#xff0c;因此&#xff0c;考虑使用CLion IDE&…

【数字孪生平台】使用 Three.js 以 3D 形式可视化日本新宿站地图

在本文中&#xff0c;我们将使用日本新宿站的室内地图数据&#xff0c;并使用 Three.js 将其进行 3D 可视化。更多精彩内容尽在数字孪生平台。 使用的数据 这次&#xff0c;我们将使用日本空间信息中心发布的“新宿站室内地图开放数据”的集成版本&#xff08;ShapeFile&#…

网络安全新视角:数据可视化的力量

在当今数字化时代&#xff0c;网络安全已成为各大企业乃至国家安全的重要组成部分。随着网络攻击的日益复杂和隐蔽&#xff0c;传统的网络安全防护措施已难以满足需求&#xff0c;急需新型的解决方案以增强网络防护能力。数据可视化技术&#xff0c;作为一种将复杂数据转换为图…

Weblogic10.3.6补丁升级

由oracle官方发布的漏洞公告&#xff0c;对weblogic进行补丁升级。本文介绍的是weblogic安装的版本为10.3.6.0&#xff0c;如果你使用的是其他版本的请根据实际版本来选择下载 对于weblogic补丁升级&#xff0c;具体步骤如下&#xff1a; 1、首先需要下载weblogic补丁程序 2…

简单了解原型模式

什么是原型模式 区别于单例模式&#xff0c;原型模式的一个类可以有多个实例化的对象。 原型模式通过拷贝来产生新的对象&#xff0c;而不是new&#xff0c;并且可以根据自己的需求修改对象的属性。 实现Cloneable接口实现拷贝 而拷贝又分为浅拷贝和深拷贝&#xff0c;两者在…

AI赋能微服务:Spring Boot与机器学习驱动的未来应用开发

&#x1f9d1; 作者简介&#xff1a;阿里巴巴嵌入式技术专家&#xff0c;深耕嵌入式人工智能领域&#xff0c;具备多年的嵌入式硬件产品研发管理经验。 &#x1f4d2; 博客介绍&#xff1a;分享嵌入式开发领域的相关知识、经验、思考和感悟。提供嵌入式方向的学习指导、简历面…

HarmonyOS入门--页面和自定义组件生命周期

文章目录 页面和自定义组件生命周期页面生命周期组件生命周期生命周期的调用时机 页面和自定义组件生命周期 生命周期流程如下图所示&#xff0c;下图展示的是被Entry装饰的组件&#xff08;首页&#xff09;生命周期。 自定义组件和页面的关系&#xff1a; 自定义组件&…

[flask]http请求//获取请求体数据

import jsonfrom flask import Flask, requestapp Flask(__name__)app.route("/form1", methods["post"]) def form1():"""获取客户端请求的请求体[表单]:return:""""""获取表单数据请求url&#xff1a;&qu…

网络安全卷么?

官方宣传的是对的网络安全现在是朝阳行业&#xff0c;缺口是很大 不过网络安全行业就是需要技术很多的人达不到企业要求才导致人才缺口大 初级的现在有很多的运维人员转网络安全&#xff0c;初级也会慢慢的卷起来&#xff0c;但是岗位多不用怕&#xff0c;以后各大去也都会要网…

Git Fork后的仓库内容和原仓库保持一致

Git Fork后的仓库内容和原仓库保持一致 ①Fork原仓库内容到自己仓库 ②将项目内容下载到本地 ③使用git命令获取原仓库内容&#xff0c;将原仓库的最新内容合并到自己的分支上并推送 下面从第三步开始演示~ 这里以码云上的若依项目为演示项目 ③使用git命令获取原仓库内容 …

基于龙芯2k1000 mips架构ddr调试心得(二)

1、内存控制器概述 龙芯处理器内部集成的内存控制器的设计遵守 DDR2/3 SDRAM 的行业标准&#xff08;JESD79-2 和 JESD79-3&#xff09;。在龙芯处理器中&#xff0c;所实现的所有内存读/写操作都遵守 JESD79-2B 及 JESD79-3 的规定。龙芯处理器支持最大 4 个 CS&#xff08;由…

Spring Transaction 指定事务管理器问题

一&#xff0c;单个数据源&#xff0c;单个事务管理器与Transactional默认事务管理器名称不一致问题 在平时代码中使用声明性事务时&#xff0c;直接在方法上面加注解即可&#xff0c;如下 Transactional(rollbackFor Exception.class) 并没有指定事务管理器&#xff0c;为…