C++入门篇(1)

目录

1. C++关键字

2. 命名空间

 2.1 命名空间的定义

2.2 命名空间的访问

2.3 命名空间的使用

3. C++的输入和输出

4. 缺省参数

4.1 缺省参数的概念

4.2 缺省参数的分类

5. 函数重载

5.1 函数重载的概念

 5.2 函数重载的原理

5.3 C语言内部函数名修饰规则

5.4 C++内部函数名修饰规则

6. 引用

6.1 引用的概念

6.2 引用的特征

6.3 引用的使用场景

6.4 传值、传引用效率

6.5 引用和指针的区别


1. C++关键字

             C++合计共有63个关键字,而C语言一共有32个关键字,如下:

2. 命名空间

  • 在C语言中,当函数名,变量名存在冲突的时候,C语言没有很好的办法来解决这一问题,例如以下一段代码:
#define  _CRT_SECURE_NO_WARNINGS 1
#include <stdio.h>
#include <stdlib.h>

int rand = 10;

int main()
{
	printf("%d\n", rand);
	return 0;
}
  • 在stdlib.h 这个头文件中,存在rand()函数,与全局变量的 rand 存在命名冲突,因此在编译过程中会报错:

  • 因为rand()函数本质上是一个地址,编译器希望以%p的形式打印,而不是%d
  • 然而当以%p的形式输出的时候又会报重定义的错误:

  • 为了解决这个问题,C++中采用了 命名空间这个方法。

 2.1 命名空间的定义

        定义命名空间,需要使用到namespace关键字,后面跟命名空间的名字,然后接一对{}可,{}中即为命名空间的成员,例如:

namespace D1
{
	int rand = 10;
}

        命名空间中可以定义变量,函数,类型,例如:

namespace D1
{
	// 命名空间中可以定义变量/函数/类型
	int rand = 10;
	int Add(int left, int right)
	{
		return left + right;
	}

	struct Node
	{
		struct Node* next;
		int val;
	};
	
}

2.2 命名空间的访问

  •         那么命名空间应该如何访问呢?这时需要引用一个操作符  :: 域作用限定符。
  •         在C语言中当全局变量和局部变量存在冲突的时候,编译器的寻找顺序是先在局部寻找,如果找不到就会在全局中寻找,例如:

  • 那如何访问全局中的变量呢?这时候就要用到 域作用限定符:

当与作用限定符前没有命名空间的名字的时候,默认访问全局。 

        类似的,如果 域作用限定符前有命名空间,那么就会访问此命名空间中的内容,例如:

2.3 命名空间的使用

  •  实际上,命名空间一共有三种使用方法,
  •  第一种方法刚才已经提到,就是 命名空间名 :: 参数 ;
  •  第二种就是 使用using namespace 命名空间名称引入,例如:
using namespace D1;


int main()
{
	int x = 6;
	//struct D1::Node phead;
	std::cout << D1::Add(10, 20) << std::endl;
	//printf("%d\n", D1::x);
	return 0;
}
  • 第三种就是使用using将命名空间中某个成员引入,例如:
//test.h
namespace D1
{
	int a = 10;
	int Add(int left, int right)
	{
		return left + right;
	}

	struct Node
	{
		struct Node* next;
		int val;
	};

}
// test.c
using D1::a;
int main()
{
	std::cout << a << std::endl;
	return 0;
}

3. C++的输入和输出

#include<iostream>
// std是C++标准库的命名空间名,C++将标准库的定义实现都放到这个命名空间中
using namespace std;
int main()
{
	cout << "Hello world!!!" << endl;
	return 0;
}
#include<iostream>
// std是C++标准库的命名空间名,C++将标准库的定义实现都放到这个命名空间中
using namespace std;
int main()
{
	const char* str = "Hello World!!!";

	const char* str1 = "你好世界!";

	int i = 10 >> 1;
	int h = 0;
	cin >> i >> h;
	cout << i << " " << h << endl;
	cout << str << " " << str1 << endl;
	return 0;
}

  • 使用cout标准输出对象(控制台)和cin标准输入对象(键盘)时,必须包含< iostream >头文件以及按命名空间使用方法使用std。
  • 2. cout和cin是全局的流对象,endl是特殊的C++符号,表示换行输出,他们都包含在包含<iostream>头文件中。
  • 3. << 是流插入运算符,>> 是流提取运算符。

4. 缺省参数

4.1 缺省参数的概念

        缺省参数是声明或定义函数时为函数的参数指定一个缺省值。在调用该函数时,如果没有指定实参则采用该形参的缺省值,否则使用指定的实参。
 

int Add(int x = 0, int y = 100)
{
	return x + y;
}

int main()
{
	int a = 10;
	int b = 10;
	int ret1 = Add(a, b);
	int ret2 = Add(a);
	cout << "ret1 = " << ret1 << endl << "ret2 = " << ret2 << endl;
	return 0;
}

4.2 缺省参数的分类

  • 全缺省参数
int Add(int x = 0, int y = 100)
{
	return x + y;
}

int main()
{
	int a = 10;
	int b = 10;
	int ret1 = Add(a, b);
	int ret2 = Add(a);
	int ret3 = Add();
	cout << "ret1 = " << ret1 << endl << "ret2 = " << ret2 << endl;
	return 0;
}

  •  半缺省参数
void Func(int a, int b = 10, int c = 20)
{
	cout << "a = " << a << endl;
	cout << "b = " << b << endl;
	cout << "c = " << c << endl;
}

int main()
{
	Func(10);
	cout << endl;
	Func(10, 20);
	cout << endl;
	Func(20, 40, 60);
	return 0;
}

        当我们在写一些程序或者项目的时候喜欢把函数声明和定义分开写,那么能否在函数声明和定义的时候同时缺省参数呢?答案是不行的。

        编译器在编译函数的时候,会经历四个过程,分别是 预处理,编译,汇编,链接

  • 预处理:预处理过程会将 .c /.cpp 文件生成 .i 文件,此时会处理程序中的 宏,注释               以及头文件的展开 等预处理信息。               
  • 编译: 编译过程会将 .i 文件生成 .s 文件,并且会将所有的语言翻译成汇编语言,翻译                的同时便会检查语法是否正确,正常会有以下阶段:词法分析、语法分析、语义              分析、符号汇总。这里的 符号汇总 就会整理文件中所有出现的函数。(此时只              会检查函数的声明是否正确。
  • 汇编:汇编过程会将 .s 文件 生成 .o 文件,同时会将汇编语言转化成机器能识别的二进             制代码,生成目标文件,并将符号整理到表格中,生成符号表
  • 链接:链接的过程会生成.out文件,在gcc的环境下,目标文件通常是按照 elf 的格式存            放的,此时会把目标文件合并,即合并段表,其次还会把所有的 符号汇总。

        缺省参数不能在函数声明和定义的时候同时给,一般会在函数声明的时候给,因为在编译的过程中,编译器会检查语法,如果存在这个函数的声明,则编译的时候会通过,但是不会找到函数的地址,函数的地址是什么呢?函数在编译的过程中会被会变成一条条指令,那函数的地址就是这些指令的第一条指令的地址。只有在链接的过程中,会将所有的目标文件和所有的符号汇总,通过符号表找到对应的函数的地址。

比如以下代码:


void Func(int a, int b = 10, int c = 20)
{
	cout << "a = " << a << endl;
	cout << "b = " << b << endl;
	cout << "c = " << c << endl;
}

int main()
{
	Func(10);
	cout << endl;
	Func(10, 20);
	cout << endl;
	Func(20, 40, 60);
	return 0;
}

一定要注意:

  • 半缺省参数必须从右往左依次来给出,不能间隔着给;
  • 缺省参数不能在函数声明和定义中同时出现。


5. 函数重载

5.1 函数重载的概念

我们知道在C语言中不允许同名参数的出现,在C++中,出现了重载的概念: 

  • 函数重载:函数重载是函数的一种特殊情况,C++允许在同一作用域中声明几个功能类                    似的同名函数,这些同名函数的形参列表(参数个数 或 类型 或 类型顺序)不                      同,常用来处理实现功能类似数据类型不同的问题。
  •  函数参数类型不同
// 1、参数类型不同
int Add(int left, int right)
{
	cout << "int Add(int left, int right)" << endl;
	return left + right;
}
double Add(double left, double right)
{
	cout << "double Add(double left, double right)" << endl;
	return left + right;
}

  •  函数参数个数不同
// 2、参数个数不同
void f()
{
	cout << "f()" << endl;
}
void f(int a)
{
	cout << "f(int a)" << endl;
}
  •  函数参数类型顺序不同
// 3、参数类型顺序不同
void f(int a, char b)
{
	cout << "f(int a,char b)" << endl;
}
void f(char b, int a)
{
	cout << "f(char b, int a)" << endl;
}

 5.2 函数重载的原理

        我刚才提到过,每个源文件都会通过编译器生成目标文件,例如现在有两个.cpp文件,一个是a.cpp另一个是b.cpp,当我们要从a.cpp文件中调用b.cpp文件中的 某个函数时,在编译之后链接之前a.o中都不会有这个函数的地址,这个函数的地址存放在b.o中,那么怎么办呢?

        这个时候会通过链接的过程,才能找到这个函数的地址,链接器看到a.o要调用这个函数,但是在本文件中并没有这个函数的定义,就会到b.o中找到这个函数的地址,然后连接到一起。

        那么链接的时候,编译器会通过什么名字去寻找呢?如果函数的名字都一样,那么在底层是如何找到我们需要调用的函数的呢?

5.3 C语言内部函数名修饰规则

 我们以Linux下的gcc为例:

  • 这是我在gcc的环境下编写的一段C代码用一下命令进行编译:
gcc -o testc test.c
  • 生成以下文件:

  • 再输入以下命令查看汇编代码:
objdump -S testc
  • 就可以查看函数的命名方式:

5.4 C++内部函数名修饰规则

  •  类似的,我又写了一段C++代码:

  • 再使用以下命令进行编译:
g++ -o testcpp test.cpp
  • 生成以下文件:

  • 再使用以下命令查看汇编代码:
objdump -S testcpp
  • 查看C++函数命名方式:

所以很容易看出来:

  • 在Linux环境下,gcc编名和程序员本身取得名字一样;
  • 在Linux环境下,g++编名函数名修饰方式发生变化,会以_Z开头,函数名的字节数放在后面,之后就是函数的返回类型的字母的缩写。 

当然,在Windows环境下,C++函数名字修饰规则更加复杂,但道理都是类似的


6. 引用

6.1 引用的概念

        引用不是新定义一个变量,而是给已存在变量取了一个别名,编译器不会为引用变量开辟内存空间,它和它引用的变量共用同一块内存空间。

  • 类型& 引用变量名(对象名) = 引用实体,例如:
void TestRef()
{
	int a = 10;
	int& ra = a;//<====定义引用类型
	printf("%p\n", &a);
	printf("%p\n", &ra);
}

6.2 引用的特征

  1. 引用在使用的时候必须初始化
  2. 一个变量可以有多个引用
  3. 引用一旦引用一个实体,之后就不可以引用其他实体

6.3 引用的使用场景

  • 引用做参数 

示例1:

void Swap(int& x, int& y)
{
	int tmp = x;
	x = y;
	y = tmp;
}

int main()
{
	int a = 10, b = 20;
	cout << "a = " << a << " " << "b = " << b << endl;
	Swap(a, b);
	cout << "a = " << a << " " << "b = " << b << endl;
}

示例2:        

        例如我们在写链表的时候一般会使用二级指针传参来改变plist,但是有了C++的引用,就可以把引用传过去:

typedef struct Node
{
	struct Node* next;
	struct Node* prev;
	int val;
}LNode, *PNode;

void PushBack(PNode& phead, int x)
{
	
}

int main()
{
	PNode plist = NULL;

	return 0;
}
  • 引用做返回值

示例1:

        如果函数有返回值,那在返回的过程中不会直接返回那个变量,而是把那个变量临时拷贝到另一个变量中,并且这个变量通常是不可变类型的,例如: 

int Add(int x = 0, int y = 0)
{
	int ret = x + y;
	//因为函数出了栈帧就会销毁,所以这个值通常会被存在另一个临时变量中
	return ret; 
}

        但如果以引用的形式返回,就会大大提高效率,因为引用只是别名,语法上不会开辟另一个空间来存放返回值 。

int& Add(int x = 0, int y = 0)
{
	static int ret = x + y;
	return ret;
}

        注意这里定义的是一个static类型的变量,如果定义的类型只是普通的int类型,因为出了函数栈帧就会被销毁那么这块int 的空间就会被重新分配给操作系统,就变成了野引用,而static类型不是存放在栈中,因此出了函数也不会被销毁。

6.4 传值、传引用效率

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

#include <time.h>
struct A { int a[10000]; };
void TestFunc1(A a) {}
void TestFunc2(A& a) {}
void TestRefAndValue()
{
	A a;
	// 以值作为函数参数
	size_t begin1 = clock();
	for (size_t i = 0; i < 10000; ++i)
		TestFunc1(a);
	size_t end1 = clock();
	// 以引用作为函数参数
	size_t begin2 = clock();
	for (size_t i = 0; i < 10000; ++i)
		TestFunc2(a);
	size_t end2 = clock();
	// 分别计算两个函数运行结束后的时间
	cout << "TestFunc1(A)-time:" << end1 - begin1 << endl;
	cout << "TestFunc2(A)-time:" << end2 - begin2 << endl;
}

int main()
{
	TestRefAndValue();
	return 0;
}

6.5 引用和指针的区别

  • 在语法概念上引用就是一个别名,没有独立空间,和其引用实体共用同一块空间

int main()
{
	int a = 10;
	int* pa = &a;
	int& ra = a;
	ra = 20;
	*pa = 30;
	return 0;
}
  •  在底层实现上实际是有空间的,因为引用是按照指针方式来实现的

        C++的引用,对指针使用比较复杂的场景进行一些替换,让代码简单易懂,但不能完全替代指针,原因是引用定义后,不能改变指向。

区别如下:

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

python 如何使用 NLPchina 开源sql插件,提供代码

分享一段使用python&#xff0c;通过使用发送post请求的方式&#xff0c;来从es集群中获取数据。不用使用 elasticsearh&#xff0c;仅需要导入request和json包即可。 开源sql插件官方 文档 GitHub - NLPchina/elasticsearch-sql: Use SQL to query Elasticsearch 示例代码 调…

【数据结构六】图文结合详解二叉树(五千字)

二叉树 树是一种非线性的数据结构&#xff0c;它是由n个结点组成的具有层次关系的集合&#xff0c;把他叫做树是因为它的根朝上&#xff0c;叶子朝下&#xff0c;看起来像一颗倒挂的树。二叉树是一种最多只有两个节点的树型结构。这篇文章会用Java代码手撕二叉树的实现&#xf…

基于YOLOv8/YOLOv7/YOLOv6/YOLOv5的木材表面缺陷检测系统(深度学习+Python代码+UI界面+训练数据集)

摘要&#xff1a;开发高效的木材表面缺陷检测系统对于提升木材加工行业的质量控制和生产效率至关重要。本篇博客详细介绍了如何运用深度学习技术构建一个木材表面缺陷检测系统&#xff0c;并提供了完整的实现代码。该系统采用了强大的YOLOv8算法&#xff0c;并对YOLOv7、YOLOv6…

汽车大灯汽车尾灯破裂裂纹破损破洞掉角崩角等问题能修复吗?修复后灯罩颜色和之前相比有什么变化?

答案是肯定的&#xff0c;汽车大灯汽车尾灯破裂裂纹破损破洞掉角崩角等问题是可以修复的。 修复后的汽车灯罩颜色可能会与之前有所不同&#xff0c;这主要取决于修复的方法和使用的材料。 首先&#xff0c;如果修复过程中使用了喷漆翻新&#xff0c;那么灯罩的颜色可能会与原来…

工业涂装行业的物联网解决方案

工业涂装行业的物联网解决方案 工业涂装行业在制造业中占据重要地位&#xff0c;其产品质量直接影响到最终产品的外观和性能。然而&#xff0c;传统涂装生产线容易出现质量问题&#xff0c;如色差、光泽度不均、橘皮现象等。为了解决这些问题&#xff0c;工业涂装行业需要寻求…

一条 sql 语句可能导致的表锁和行锁以及死锁检测

锁 MDL 当对一个表做增删改查操作的时候&#xff0c;加 MDL 读锁&#xff1b;当要对表做结构变更操作的时候&#xff0c;加 MDL 写锁 ALTER TABLE tbl_name NOWAIT add column ... ALTER TABLE tbl_name WAIT N add column ... …

2000-2023年7月全国各省专利侵权结案案件数量数据

2000-2023年7月全国各省专利侵权结案案件数量数据 1、时间&#xff1a;2000-2023年7月 2、指标&#xff1a;地区、年份、专利侵权纠纷行政案件-结案数目 3、范围&#xff1a;31省 4、来源&#xff1a;国家知识产权局&#xff0c;并由该局每个月公布的数据汇总而成 5、指标…

Linux学习笔记(一)Linux基本指令

文章目录 前言目录常见命令1. pwd 打印当前所在路径2. cd 改变路径、切换路径3. 家目录 回到顶级目录4. 当前路径和上一路径5. 上一次路径6. 绝对路径和相对路径7. ls 列出目录内容8. mkdir 创建目录9. rmdir 删除目录10. touch 创建文件11. mv 修改文件目录、移动路径12. cp 复…

12、设计模式之代理模式(Proxy)

一、什么是代理模式 代理模式属于结构型设计模式。为其他对象提供一种代理以控制对这个对象的访问。 在某些情况下&#xff0c;一个对象不适合或者不能直接引用另一个对象&#xff0c;而代理对象可以在客户端和目标对象之间起到中介的作用。 二、分类 代理模式分为三类&#…

java-单列集合-set系列

set集合继承collection,所以API都差不多&#xff0c;我就不多加介绍 直接见图看他们的特点 我们主要讲述的是set系列里的HashSet、LinkedHashSet、TreeSet HashSet HashSet它的底层是哈希表 哈希表由数组集合红黑树组成 特点&#xff1a;增删改查都性能良好 哈希表具体是…

Seata 2.x 系列【8】Spring Cloud 集成客户端

有道无术&#xff0c;术尚可求&#xff0c;有术无道&#xff0c;止于术。 本系列Seata 版本 2.0.0 本系列Spring Boot 版本 3.2.0 本系列Spring Cloud 版本 2023.0.0 源码地址&#xff1a;https://gitee.com/pearl-organization/study-seata-demo 文章目录 1. 前言2. 问题演…

如何解决跨网文件交换行为分散难管控等问题?

跨网文件交换是指在不同的网络环境之间&#xff0c;如内网和外网&#xff0c;安全地传输文件的过程。这通常涉及到网络隔离的场景&#xff0c;比如政府机构、金融机构、大型企业等&#xff0c;它们为了安全和保密的需要&#xff0c;会通过物理隔离、逻辑隔离等方式&#xff0c;…

《向量数据库指南》——Milvus Cloud BYOC:为数据安全而生?

最近,整个硅谷都在关注 OpenAI 和 Anthropic 的动态。先是 Anthropic 发布了 Claude 3,剑指 GPT-4,被媒体认为“打破了 OpenAI 不可战胜的神话”。这也点燃了整个科技圈的热情,纷纷期待 OpenAI 放出 GPT-5 应战。随后(美东时间 3 月 5 日),OpenAI 发布一则官方公告,主题…

算法打卡day14|二叉树篇03|104.二叉树的最大深度、559.n叉树的最大深度、111.二叉树的最小深度、222.完全二叉树的节点个数

算法题 Leetcode 104.二叉树的最大深度 题目链接:104.二叉树的最大深度 大佬视频讲解&#xff1a;二叉树的最大深度视频讲解 个人思路 可以使用层序遍历&#xff0c;因为层序遍历会有一个层数的计算&#xff0c;最后计算到的层数就是最大深度&#xff1b; 解法 迭代法 就是…

基于SWOT的智能手机企业财务战略研究1.62

摘 要 近些年&#xff0c;网络技术日新月异&#xff0c;智能手机深受消费者喜爱&#xff0c;人们通过网络&#xff0c;手机应用&#xff0c;可以极大地方便人们学习&#xff0c;工作等等。由于国家对电信行业的大力支持&#xff0c;中国消费者群体逐步成为最具潜力的手机购买者…

基于单片机的IC 卡门禁系统设计

摘要:针对传统门锁钥匙易丢失、配置不便和忘记携带等问题,提出了一种基于STC89C52 的IC 卡门禁系统设计。该系统以STC89C52 单片机为核心来控制电子锁模块的开关。主要过程是由RFID 模块读取IC卡ID 并通过串口发送至STC89C52 单片机模块,STC89C52 单片机模块可以实现在线对I…

基于YOLOv8/YOLOv7/YOLOv6/YOLOv5的吸烟检测系统(深度学习+Python代码+PySide6界面+训练数据集)

摘要&#xff1a;本文详细说明了如何利用深度学习开发一个用于监测吸烟行为的系统&#xff0c;并分享了完整的代码实现。该系统采用了先进的YOLOv8算法&#xff0c;同时还使用YOLOv7、YOLOv6、YOLOv5算法&#xff0c;并对它们进行了性能比较&#xff0c;呈现了不同模型的性能指…

VS中配置生成事件

一、为什么需要使用生成事件&#xff1f; 在实际开发过程中&#xff0c;在项目生成DLL后&#xff0c;需要被复制到不同的目录下被引用&#xff0c;很麻烦。 我们可以利用VS中的项目生成事件属性来进行生成后的DLL复制到指定的目录&#xff0c;或者进去其他的操作&#xff0c;比…

C/C++——Tchisla求解器(多线程高性能版本)

前言 之前一篇文章中介绍的使用Python写的Tchisla求解器Python——Tchisla求解器&#xff08;暴力搜索法&#xff09;在我实际使用中有比较大的缺陷&#xff0c;首先是太慢了&#xff0c;对于每日一题中四位数的目标数字&#xff0c;往往搜索数个小时都找不完1~9的全部最优解&…

重学SpringBoot3-ErrorMvcAutoConfiguration类

更多SpringBoot3内容请关注我的专栏&#xff1a;《SpringBoot3》 期待您的点赞&#x1f44d;收藏⭐评论✍ 重学SpringBoot3-ErrorMvcAutoConfiguration类 ErrorMvcAutoConfiguration类的作用工作原理定制 ErrorMvcAutoConfiguration示例代码1. 添加自定义错误页面2.自定义错误控…