【C++从0到1】新手都能看懂的C++入门(上篇),建议收藏

c++入门必看

  • 前言
  • 一、命名空间
  • 二、缺省函数
  • 三、 函数重载
    • 3.1常见使用
    • 3.2重载底层实现
    • 3.3返回值不纳入重载原因
  • 四、extren C
  • 五、引用
    • 5.1常见使用
    • 5.2临时变量的作用
    • 5.3引用返回常见错误
    • 5.4指针与引用
  • 六、内联函数
    • 内联函数替代宏的原因
  • 七、nullptr
  • 总结


前言

C++入门是很多人头疼的问题,今日就与博主一同学习一些比较简单的基本概念,这些在之后很多地方的作用是十分大的。
看完之后,相信你会受益匪浅!!


一、命名空间

命名空间是什么,为什么要存在命名空间,首先我们如果以前有看过c++的代码一定少不了这两句。

#include
using namespace std;

这个大家想必都看过,using在这里实际上就是展开命名空间的内容。

命名空间就是一个域,与外界隔离,使用域当中的东西要通过(域名+::)【C++从0到1】新手都能看懂的C++入门(上篇),建议收藏_第1张图片【C++从0到1】新手都能看懂的C++入门(上篇),建议收藏_第2张图片

当我们要使用当前引入库函数当中的函数名作为变量时,例如:如果我们引入头文件#include,我们会发现我们使用rand作为变量名会出错【C++从0到1】新手都能看懂的C++入门(上篇),建议收藏_第3张图片

这是因为我们rand在stdlib中是作为一个函数存在的,我们如果引了头文件,相当于在预处理的时候就会将头文件展开,我们就会看到rand作为函数的声明及实现。【C++从0到1】新手都能看懂的C++入门(上篇),建议收藏_第4张图片

这个时候我们就无法使用这个rand,命名空间却能帮我们解决【C++从0到1】新手都能看懂的C++入门(上篇),建议收藏_第5张图片

从上图可以看出,命名空间不受到其他域当中影响,因为在全局域展开并不会影响到命名空间。但是如果我们不展开命名空间的话,调用命名空间内部的函数时我们都要采用域限定符,所以我们在进行一些日常练习时才会去进行命名空间的展开。
【C++从0到1】新手都能看懂的C++入门(上篇),建议收藏_第6张图片
【C++从0到1】新手都能看懂的C++入门(上篇),建议收藏_第7张图片

需要注意的就是,如果有同名的命名函数,他们就会被合在一起。【C++从0到1】新手都能看懂的C++入门(上篇),建议收藏_第8张图片
对于命名空间,内部也可以在嵌套定义命名空间,使用的时候也要注意加上域作用限定符。【C++从0到1】新手都能看懂的C++入门(上篇),建议收藏_第9张图片

总结:我们使用命名空间的时候可以进行部分展开,对于常用的函数展开,这是一种折中的解决方案。【C++从0到1】新手都能看懂的C++入门(上篇),建议收藏_第10张图片


二、缺省函数

常见使用【C++从0到1】新手都能看懂的C++入门(上篇),建议收藏_第11张图片
【C++从0到1】新手都能看懂的C++入门(上篇),建议收藏_第12张图片

在模拟顺序表的初始化的时候,我们可以用缺省参数控制我们要申请多大的空间,如果没有传参数,我们可以默认帮它设置。其中我们上面演示的为半缺省参数。

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

解释第一条:因为如果从左开始给的话,如果我们左边想要默认的缺省值,就不合理了。
解释第二条:倘若声明和定义当中给的缺省值不一样,编辑器无法判断,所以制止这种写法,倘若要使用,则参照下图。【C++从0到1】新手都能看懂的C++入门(上篇),建议收藏_第13张图片


三、 函数重载

3.1常见使用

【C++从0到1】新手都能看懂的C++入门(上篇),建议收藏_第14张图片
刚刚谈完的命名空间中一个域中不能出现重复的定义,但是c++在这里,允许了函数重载,只要函数的顺序(不同类型之间),类型不同,都可以造成函数重载。

上面这三种均不造成重载,现在来说明原因。原因与c++当中为何支持函数重载有关,相信看了下面你就能明白。


3.2重载底层实现

提前说明,以下为linux的解释说明,并且文件后缀不代表他是否为cpp文件,而是根据我们所调用的编辑器决定的。
先说结论:是因为他们的函数名修饰规则不同导致的。

我们先来看看linux下对于c的函数名和cpp当中的函数名在编译阶段是怎样的,这里我们用的是objudump -S,查看我们的.o文件

我们能在这里观察到对于linux平台下,链接时我们的函数是没有经过任何修饰的,通过这样的函数名在链接的时候重定位将函数的地址再填进去。在c中,这样子当我们重载的时候,两个同名的add即便构成重载,却因为两个相同的add构成了不确定导致无法进行链接【C++从0到1】新手都能看懂的C++入门(上篇),建议收藏_第15张图片
而c++当中对于函数进行了修饰,并且它的修饰规则比较简单_Z是固定的前缀,3表示函数名的长度,我们Add的长度正好是3,而剩下的ii/dd则就是参数名称。
【C++从0到1】新手都能看懂的C++入门(上篇),建议收藏_第16张图片
【C++从0到1】新手都能看懂的C++入门(上篇),建议收藏_第17张图片


3.3返回值不纳入重载原因

那么有人可能会想,为什么设计的时候不把返回值也作为函数重载的依据呢,设想一下,假如我们在函数名前面假如返回值的首字母作为修饰函数名,下面这种情况是不是也是存在异议的,也就是我们底层设计的时候虽然是可以支持到函数返回值也来修饰函数名,从语法层面,编辑器不能通过此特征来判断调用哪个函数。

【C++从0到1】新手都能看懂的C++入门(上篇),建议收藏_第18张图片


四、extren C

我们在日常中如果采用c语言去写一个项目,如果我们要利用c++库的话,根据上面函数重载,我们c语言在链接时通过自己的函数名是没办法找到对应的c++的函数名的,而c++在c语言后面诞生,所以采用c++写的项目,调用c的库的时候是可以直接找到的。所以我们编写c++的库的时候,为了照顾到我们用纯c写项目的,就会在该函数的声明处加上extern “C”,这样c++和c都能同时调到这个函数。

比如谷歌提供tcmalloc库中就有若干函数如此。

【C++从0到1】新手都能看懂的C++入门(上篇),建议收藏_第19张图片


五、引用

5.1常见使用

【C++从0到1】新手都能看懂的C++入门(上篇),建议收藏_第20张图片
可以看出a为b的一个引用,别名,语法上是不占空间的,我们通过取地址也可以看出a和b指向的都是同一块内存。

【C++从0到1】新手都能看懂的C++入门(上篇),建议收藏_第21张图片
引用的特点:引用必须初始化。引用只能对一块空间,不像指针可以转换指向内容。 一个变量可以有多个引用。


观察下面代码:

int main()
{
     
//这里是b赋值给a,还是a是b的引用呢?
	int b = 10;
	int& a = b;
	int c = 20;
	a = c;
	return 0;
}

答案揭晓:这里是b赋值给a。
【C++从0到1】新手都能看懂的C++入门(上篇),建议收藏_第22张图片
因为我们接下来要讲到引用返回会遇到的种种情况,所以我们在这里先讲述临时变量是一个怎样的存在。


5.2临时变量的作用

思考这里的ret是怎么返回回去的,在函数栈帧中提到了调用完函数之后,空间会被释放,我们return ret 的时候他是将ret返回吗?不是的,因为ret在Add的栈帧中,如果他返回就会造成空间的越界访问。那么他是如何返回的呢。

答案:当返回的数据大小在4,8字节的情况下通常是由我们的寄存器eax带回来,如果超过这个范围的话,就会在main函数的栈帧中提前开设一块跟返回值的一份临时拷贝。临时数据具有常性。【C++从0到1】新手都能看懂的C++入门(上篇),建议收藏_第23张图片
【C++从0到1】新手都能看懂的C++入门(上篇),建议收藏_第24张图片

带回我们的结构体做了若干操作,我们这里不详细讲解。对上面的答案进行简单的论证。

【C++从0到1】新手都能看懂的C++入门(上篇),建议收藏_第25张图片

强制类型转换和隐式类型转换都是会创建一个临时变量进行接受的,像下面这张图,我们的r虽然是i的引用,但是他们却不是指向同一块空间的,而这里的r是指向谁了呢?就是我们的一个临时变量,临时变量具有常性,我们不能对他进行修改,所以我们要添加const表示只对该变量有读取的权限!
【C++从0到1】新手都能看懂的C++入门(上篇),建议收藏_第26张图片

常引用如下:
【C++从0到1】新手都能看懂的C++入门(上篇),建议收藏_第27张图片

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

引用和指针在sizeof中含义不同:引用结果为引用类型的大小(与类型有关),但指针始终是地址空间所占字节个数(32位平台下占4个字节),即与平台有关。


5.3引用返回常见错误

引用有作为参数,和返回值的两大作用,那么使用的时候返回值这块容易犯错误。

int& Add(int x, int y)
{
     
	int ret = x + y;
	return ret;
}
int main()
{
     
	int sum = Add(1, 2);
	Add(3, 4);
	cout << "Add(1,2): is " << sum << endl;
	return 0;
}

虽然上面的代码运行的结果是正确的,但是存在着很大的安全隐患,Add这个栈帧销毁过后再去访问,倘若系统对于回收的空间会进行处理的话,这块空间上的值就是随机值,vs2019在这块对于回收的内存是没有做处理的。
【C++从0到1】新手都能看懂的C++入门(上篇),建议收藏_第28张图片
知道了上面的结论,再来看下面这段代码:只改了这一处返回值,这时候会发生什么呢?

【C++从0到1】新手都能看懂的C++入门(上篇),建议收藏_第29张图片
【C++从0到1】新手都能看懂的C++入门(上篇),建议收藏_第30张图片
【C++从0到1】新手都能看懂的C++入门(上篇),建议收藏_第31张图片

甚至当你在Add那块空间被cout给覆盖之后,ret的内存空间甚至有可能是一些奇奇怪怪的值。
【C++从0到1】新手都能看懂的C++入门(上篇),建议收藏_第32张图片

结论:对于引用返回,返回的对象必须是栈帧销毁后还存在的。全局,静态,未销毁的函数栈帧当中的都是可以的!!

5.4指针与引用

如图:两者底层实现差不多,引用是用指针模拟的。
【C++从0到1】新手都能看懂的C++入门(上篇),建议收藏_第33张图片


六、内联函数

inline是一种以空间换时间的做法,省去调用函数额开销。所以代码很长或者有循环/递归的函数不适宜 使用作为内联函数。
inline对于编译器而言只是一个建议,编译器会自动优化,如果定义为inline的函数体内有循环/递归等 等,编译器优化时会忽略掉内联。
inline不建议声明和定义分离,分离会导致链接错误。因为inline被展开,就没有函数地址了,链接就会找不到。

内联函数替代宏的原因

#define SWAP(T,m,n) {
       T s; s=m, m=n, n=s;}
//#define Add(a,b) a+b
//#define Add(a,b) (a+b)
//#define Add(a,b) ((a)+(b))

int main()
{
     
	int a = 0;
	int b = 1;
	//宏的本质是替换,得看替换之后合不合理!!
	//{T s; s=m, m=n, n=s;}
	SWAP(int,a,b);
	cout << a <<" "<< b << endl;
	return 0;
}

因为宏虽然功能强大,但是在使用的时候缺少类型检查,调试不方便,使用的比较别扭等因素,所以我们c++引入内联函数,在调用的地方展开。但是对编辑器只是一种建议。
c++当中建议用const,enum替换宏常量,内联函数替换宏函数。


七、nullptr

【C++从0到1】新手都能看懂的C++入门(上篇),建议收藏_第34张图片
c++标准将NULL定义为0,通常情况下按照c语言,int* p=NULL,还是没有错的,但是在下面这种情况会有异议。所以我们推荐c++当中使用nullptr作为空指针。
【C++从0到1】新手都能看懂的C++入门(上篇),建议收藏_第35张图片


总结

c++的入门就到这里啦,下一章会描述类和对象。
三连支持一下吧!!!

你可能感兴趣的:(C++,数据结构,c++)