【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,一经查实,立即删除!

相关文章

关于云墙的一些功能

功能介绍 产品功能功能解释应用识别在进行分析报文头的基础上&#xff0c;结合不同的应用协议特征库综合判断所属的应用监控统计对设备数据进行统计&#xff0c;并以柱状图、折线图、表格、报表、日志等方式呈现出来&#xff0c;帮助用户通过统计数据掌握设备状况&#xff0c;…

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 修改全选才能进入并且编写。…

如何使用python链表

在Python中&#xff0c;可以使用类来实现链表的数据结构。链表是一种数据结构&#xff0c;它由一系列节点组成&#xff0c;每个节点包含一个数据元素和一个指向下一个节点的引用。 下面是一个简单的链表类的示例&#xff1a; class Node:def __init__(self, data):self.data …

【力扣hot100】25 K个一组反转链表(c++)解析

这是一题比较简单的hard 思路和模拟方法相同&#xff0c;首先判断链表是否有K个数&#xff0c;不足则直接返回head。否则对前K个数进行反转&#xff0c;然后进行递归处理。 class Solution { public:ListNode* reverseKGroup(ListNode* head, int k) {ListNode *p head;for(i…

laravel(源码阅读):kernel过程和console调度artisan命令

http kernel 使用相关&#xff1a;通过artisan命令、计划任务、队列启动框架进行处理的 创建应用实例绑定内核到容器解析http内核实例处理http请求发送响应终止应用程序&#xff0c;中间件 terminable 善后。session写入存储器中。 Console artisan工具&#xff1a;执行计划…

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

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

移动视频-智能安全帽-5G/4G记录仪-布控球-无人机5G/4G回传产品及平台常用技术支持资料汇总

视频融合通信平台相关文档 基于webRTC构建的新一代视频融合可视指挥调度平台smarteye server WEB SDK restful接口文档&#xff0c;https://smarteye.besovideo.com/doc/ 新一代smarteye配套的WEB融合可视指挥调度平台软件常用功能&#xff0c;https://www.besovideo.com/d…

国密SM2前端加密解密

<template><div><el-button type"primary" click"getphone()">sm2加密按钮</el-button><el-button type"primary" click"returnphone()">sm2解密按钮</el-button></div> </template>…

前端 JS 压缩图片的思路(附源码)

前言 相信大家都做过图片上传相关的功能,在图片上传的过程中,不知道大家有没有考虑过文件体积的问题,如果我们直接将原图片上传,可以图片体积比较大,一是上传速度较慢,二是前端进行渲染时速度也比较慢,比较影响客户的体验感。所以在不影响清晰度的情况下,前端可以在上…

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…

第十三章 Tomcat优化

一、Tomcat 优化 Tomcat 下载地址&#xff1a;https://archive.apache.org/dist/tomcat 1.1 源码导入 1. 下载对应的 tomcat 版本源码&#xff08;Tomcat8.0.11&#xff09; 2. 源码根目录下创建 pom.xml 文件 3. 将源码导入 idea 中 1.2 Web 容器 - Connector.initIntern…

【LeetCode热题100】20. 有效的括号(栈)

一.题目要求 给定一个只包括 ‘(’&#xff0c;‘)’&#xff0c;‘{’&#xff0c;‘}’&#xff0c;‘[’&#xff0c;‘]’ 的字符串 s &#xff0c;判断字符串是否有效。 有效字符串需满足&#xff1a; 左括号必须用相同类型的右括号闭合。左括号必须以正确的顺序闭合。…

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&#…

软考倒计时58天!高效备考的方法有这些!

距离2024年上半年软考正式考试还剩58天的时间&#xff0c;留给考生的备考时间越来越少。而且有很多考生都是上班族&#xff0c;每天的学习时间非常有限。为此&#xff0c;为各位考生提供了一些高效备考方法&#xff0c;帮助大家科学备考。 01、合理分配学习时间 每个科目都有…