C++启航跟沸羊羊有关系吗?

在这里插入图片描述

大家好,我是安然无虞。

自学网站

推荐给老铁们两款学习网站:
面试利器&算法学习:牛客网
风趣幽默的学人工智能:人工智能学习
首个付费专栏:《C++入门核心技术》

写在前面

最近开了一个关于C++的付费专栏:《C++入门核心技术》 专栏主打大厂校招高频考点,助力大家直迎大厂C++方向面试,当然,如果你仅仅只是为了应付学校考试,那就更没有问题了,学完这个专栏的一部分内容就可以考个不错的成绩。
欧克,任我说得天花乱坠都没用,得拿成品出来,下面直接进入今天的主题。

分割线:梦想启航

一、关键字

我们知道C语言有32个关键字,那么C++有几个关键字呢?答案是C++有63个,C++在C语言的基础之上又补充了21个关键字,这63个关键字分别是:

asm do if return try continue
auto double inline short typedef for
bool dynamic_cast int signed typeid public
break else long sizeof typename throw
case enum mutable static union wchar_t
catch explicit namespace static_cast unsigned defualt
char export new struct using friend
class extern operator switch virtual register
const false private template void true
const_cast float protected this volatile while
delete goto reinterpret_cast

这一小节主要是让大家简单了解一下,放心吧,后面会专门详细讲解C++关键字的。

分割线:恭喜您即将掌握一个新的知识

二、命名空间

1.命名冲突

看下面一段C语言代码:

#include<stdio.h>

int rand = 0;//定义一个全局变量rand

int main()
{
    
    
	printf("%d\n", rand);
	
	return 0;
}

程序正确执行:
在这里插入图片描述
但是!如果加上stdlib.h这个头文件呢?
代码如下:

#include<stdio.h>
#include<stdlib.h>

int rand = 0;//定义一个全局变量rand

int main()
{
    
    
	printf("%d\n", rand);

	return 0;
}

这个时候执行程序会报错:
在这里插入图片描述

看错误列表怎么说:"rand"重定义,以前的定义是函数

这什么情况呀,为什么一开始还好好的,加上stdlib.h头文件之后就报错了呢?毋庸置疑的是肯定跟 stdlib.h 头文件有关系,但是具体有什么关系还得继续往下看:

#include<stdlib.h>

之所以加上这行代码之后报错,是因为在stdlib.h头文件里有一个生成随机数的库函数 rand ,结构如下:
在这里插入图片描述
因为在程序编译的时候,头文件会展开,也就会将头文件中所有的内容都放出来,此时同一作用域内出现两个同名变量,编译器会报错。这儿,便是命名冲突。
注意哦:

同一作用域内不可以出现同名变量。

由于C语言没有很好地解决命名冲突的问题,所以C++为了解决它,特意引入 namespace

2.namespace关键字

在C++中,变量、函数和类型都是大量存在的,如果全部存在于全局作用域中,可能会导致许多冲突。
使用命名空间的目的就是对标识符的名称进行本地化,以避免命名冲突或名字污染,namespace关键字的出现就是为了解决这个问题的。
好,铺垫了这么多,如何用命名空间解决上述问题呢?请看下面一段代码:

#include<iostream>
#include<stdlib.h>
using namespace std;

namespace SL
{
    
    
	int rand = 0;
}

int main()
{
    
    
	printf("%d\n", SL::rand);
	//::是域作用限定符,如果::左边没有SL,就默认是全局作用域
	return 0;
}

这样程序就正确执行了,可能大家并不理解为什么这样程序可以跑起来,OK,不理解就对了,就是因为我们有很多不理解的地方所以才要学习呀,别害怕也别着急,都会讲到的,走着。

3.命名空间的定义

定义命名空间,需要使用namespace关键字,后面跟上命名空间的名字,然后接上一对大括号即可,{ } 中即为命名空间的成员,可以定义变量、函数以及类型。

命名空间中可以定义变量、函数以及类型。

· 普通命名空间

namespace SL   //SL为命名空间的名称
{
    
    
	//命名空间中的内容,既可以定义变量、函数,也可以定义类型
	int a = 10;
	
	int Add(int x, int y)
	{
    
    
		return x + y;
	}

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

· 嵌套命名空间

namespace byte
{
    
    
	int a = 0;

	//命名空间可以嵌套,一般多是2~3层
	namespace data
	{
    
    
		struct Node
		{
    
    
			int val;
			struct Node* next;
		};
	}
}

· 同名命名空间

同一个工程中允许存在多个相同名称的命名空间,编译器最后会合并在同一个命名空间中。

namespace byte
{
    
    
	int a = 0;

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

namespace byte
{
    
    
	int b = 0;

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

注意事项:

  • 一个命名空间就定义了一个新的作用域,命名空间中的内容都局限于其中;
  • 命名空间不会影响变量的生命周期,而且命名空间的定义只能是全局的,不能在局部中定义命名空间,比如在main函数中定义就是错误的;
  • 一般命名空间是定义在头文件下的。

对于命名空间的定义做一个总结:

1. 命名空间中可以定义变量、函数以及类型;
2. 命名空间支持嵌套,防止工程太大导致命名冲突;
3. 同名的命名空间是可以同时存在的,编译器编译时会自动合并。

4.命名空间的使用

还是采用这段代码:
在这里插入图片描述
注意看上面定义的命名空间哦,瞧好了,看下面如何使用:

using namespace byte;
//意思是把byte这个命名空间展开,将其中内容全部放出来
//但是需要注意的是嵌套的命名空间中的内容并没有放出来哦
using namespace byte;
using namespace data;
//这两行代码的意思是:先展开byte,再展开data
//如果颠倒这两行代码的顺序是不可以的,而且其意义不等同于using namespace byte::data;
using namespace byte::cache;
//注意哦,这里展开的是byte中的cache,并没有将byte展开

using的意义:

将命名空间中的内容全部或部分放出来。

比如:
全部放出来:

using namespace byte;//将byte展开

部分放出来:

using namespace byte::a;//将byte中的变量a放出来

注意哦,只有放出来才能使用哦,否则还局限于命名空间中。

命名空间的三种使用方式:

· 加命名空间名称及作用域限定符

printf("%d\n", byte::a);
//这样就可以直接打印byte中的a

· 使用using将命名空间中成员引入

通常都是把部分常用的放出来。

using byte::a;//将byte中的变量a放出来

int main()
{
    
    
	printf("%d\n", a);
}

· 使用using namespace命名空间引入

这种使用方式直接将命名空间中的内容全部放了出来。

using namespace byte;

int main()
{
    
    
	printf("%d\n", a);
	printf("%d\n", b);
}

对于第3种使用方式,我们最常用的就是:

using namespace std;

这里首先跟大家说一下,这样的使用方式是存在风险的,为什么这样说呢,下面补充一条知识点:
std 是C++标准库的命名空间,将它里面的内容全部放出来虽然方便我们使用了,但是存在冲突风险,所以我们在写一些C++ IO 型题目不在乎冲突的时候可以全部放出来,平时学习C++的时候最好别全部放出来,放出部分常用的即可,这点在后面你就可以体会到了。

分割线:新的挑战迎面而来 Are you ready?

三、C++输入&输出

我们在学习C语言的时候都是这样向世界问好的:
在这里插入图片描述那么C++是如何向世界打招呼的呢?
在这里插入图片描述其中 ‘endl’ 相当于 ‘\n’,看到了吧,这里我并没有将 std 中的内容全部放出来,而是只把常用的cout 和 endl 放了出来。

对C++的输入&输出进行说明:

  1. 使用 cout 标准输出和 cin 标准输入时,必须包含 iostream 头文件以及 std 标准命名空间;
  2. 使用C++输入输出更方便,不需要增加数据格式控制,如%d, %c这些。

补充一条小知识点:

C++早期标准库将所有的功能实现在全局域中,声明在 .h 后缀的头文件中,使用时只需要包含对应的头文件即可,后来将其实现在 std 命名空间下,为了和 C 头文件区分开,也为了正确使用命名空间,规定C++头文件不带 .h 后缀。

分割线:恭喜您即将通关该小节内容

四、缺省参数 | 默认参数

缺省参数又叫默认参数,C++支持缺省参数,但是C语言不支持。那么什么又是缺省参数呢?
举一个现实生活中很形象很可悲的例子:

大家知道备胎吗?C++函数中的参数是支持配备备胎的。

没听懂?不要紧,再朝下看你就懂了(坏笑)

1.缺省参数的定义

缺省参数是声明或定义函数时为函数的参数指定一个默认值。在调用该函数时,如果没有指定实参就采用该默认值,否则使用指定的实参。
比如下面一段代码:

#include<iostream>
using std::cout;
using std::endl;

void TestFunc(int a = 10)
{
    
    
	cout << "a = " << a << endl;
}

int main()
{
    
    
	TestFunc();//没有传参时,使用参数的默认值

	TestFunc(20);//传参时,使用指定的实参

	return 0;
}

生动来说的话就是:

TestFunc();//没有传参时,使用参数的默认值
//喜羊羊不在,沸羊羊登场(默认参数)
TestFunc(20);//传参时,使用指定的实参
//喜羊羊回来了,没有沸羊羊什么事了

总结:

小时候不懂原来沸羊羊这么可怜。

2.缺省参数的分类

· 全缺省参数

全缺省参数就是参数中全部给了缺省值。比如下面的函数:

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

全缺省很简单,就不过多赘述了。

· 半缺省参数

注意哦,半缺省参数可不是参数中一半给了缺省值,哈哈,而是部分参数给了缺省值。
还有一个注意事项是:半缺省的必须从右往左缺省,中间不能有间隔,这是语法规定的。
比如下面这段代码是没有问题的:

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

但是不允许出现下面这样的写法:

void TestFunc(int a = 10, int b = 20, int c)
{
    
    
	……
}

这样也不可以:

void TestFunc(int a = 10, int b, int c = 30)
{
    
    
	……
}

所以,这个时候你应该理解上面我所说的注意事项了吧。
再次强调一遍:

半缺省参数必须从右往左给出缺省值。

还有一个需要注意的地方:
缺省参数不能在函数的声明和定义的时候同时出现,只在函数声明的时候参数带有缺省值,函数定义的时候不能再重复出现了。(针对于函数的声明和定义分开写的情况,只有函数定义时可忽略)
为什么这样说呢,请看下面一段代码:

//Func.h
//函数的声明
void TestFunc(int a = 10);
//Func.cpp
//函数的定义
void TestFunc(int a);

为什么C++语法要这样规定呢?

因为鸭,如果声明与定义同时给出缺省值,恰巧这两个值又不同,此时编译器就无法确定到底该用哪个缺省值。

打个比方吧:

比如,小明要放暑假了,他高兴地打电话回家告诉爸妈自己几号回去,结果嘞,他的爸爸说:娃呀,放假就直接回来吧,别出去乱跑,爸爸想你了。不过,小明的妈妈却说:别回来了,回来了我还要天天给你做饭洗衣服,还耽误我打麻将,去打暑假工吧,体验体验生活。

你就说,此时此刻,小明该听谁的,该何去何从呢?OK,通过这个小故事,你应该理解为什么C++语法要规定说缺省参数不能在函数声明和定义中同时出现了。
着重强调一下:

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

3. 缺省参数的用处

前面说了这么多,我还是体会不到缺省参数有什么用,OK,我想想该用我们现在所学过的什么知识去举例子?有了,半缺省参数有一个很好的实例:
比如我们之前在学习数据结构——栈,我们用C语言代码是这样写的:

#include<stdio.h>
#include<assert.h>
#include<stdlib.h>

typedef struct Stack
{
    
    
	int* a;
	int top;
	int capacity;
}Stack;

//初始化
void StackInit(Stack* ps)
{
    
    
	assert(ps);
	ps->a = NULL;
	ps->top = 0;
	ps->capacity = 0;
}
//入栈
void StackPush(Stack* ps, int x)
{
    
    
	assert(ps);
	//每次入栈都需要判断栈是否满了
	if (ps->top == ps->capacity)
	{
    
    
		int newCapacity = ps->capacity == 0 ? 4 : ps->capacity * 2;
		int* tmp = (int*)realloc(ps->a, newCapacity * sizeof(int));
		assert(tmp);

		ps->a = tmp;
		ps->capacity = newCapacity;
	}
	ps->a[ps->top] = x;
	ps->top++;
}

int main()
{
    
    
	Stack ST;
	StackInit(&ST);

	return 0;

}

但是上面的代码存在问题,每次入栈都需要判断栈是否满了,而且扩容是需要付出性能等方面的代价的。
C++的缺省参数就派上用场了,你瞧下面的代码:

#include<iostream>
#include<cassert>
#include<cstdlib>

struct Stack
{
    
    
	int* a;
	int top;
	int capacity;
};

void StackInit(Stack* ps, int n = 4)
{
    
    
	assert(ps);
	ps->a = (int*)malloc(n * sizeof(int));
	ps->top = 0;
	ps->capacity = 0;
}

int main()
{
    
    
	Stack ST;
	StackInit(&ST, 100);
	return 0;
}

当我们知道要开辟多大空间时,使用C++的缺省参数去初始化比C语言实现栈要优很多,而且直接在初始化的时候就可以将数组的空间开辟好。
缺省参数好用的地方还有很多,因为涉及到后面的知识,所以请铁子们耐住性子,后面还有很多接触它的机会呢。(狗头)

对缺省参数做一个总结:

  • C++支持缺省参数,C语言不支持;
  • 缺省参数不能在函数声明和定义时同时出现;
  • 半缺省参数必须从右往左依次给出,不能隔着给;
  • 缺省值必须是常量或者全局变量。
分割线:强化练习直击大厂面试真题

五、大厂面试真题

练习1

下面关于C++命名空间描述错误的是( )【单选】
A.命名空间定义了一个新的作用域。
B.std是C++标准库的命名空间。
C.在C++程序中,使用标准命名空间必须写成using namespace std;
D.我们可以自己定义命名空间。

解题思路

A.命名空间的作用就是定义一个新的作用域

B.std是C++中的标准命名空间

C.不是必须写成using namespace std,还有std::xx 和using std::xx来使用标准库

D.可以通过namespace space_name{};定义自己新的命名空间


练习2

下面关于C++缺省参数描述错误的是( ) 【多选】
A.缺省参数是声明或定义函数时为函数的参数指定一个默认值.
B.在调用有缺省参数的函数时,如果没有指定实参则采用该默认值,否则使用指定的实参
C.C和C++都支持缺省参数
D.全缺省就是参数全部给缺省值,半缺省就是缺省一半的值

解题思路

A.缺省参数是声明或定义函数时为函数的参数指定一个默认值,这点说的没错;
B.在调用有缺省参数的函数时,如果没有指定实参则采用该默认值,否则使用指定的实参,对的,记住喜洋洋和沸羊羊的那个例子;
C.C和C++都支持缺省参数,错,C语言不支持缺省参数;
D.全缺省就是参数全部给缺省值,半缺省就是缺省一半的值,错,全缺省就是参数全部给了缺省值,半缺省指的是参数部分给了缺省值,不是一半!

分割线:下节精彩内容预告

六、函数重载中隐藏的细节

函数重载很重要,面试的时候常考,所以我会非常非常非常仔细地讲解底层原理,放心吧铁子们。
常考的面试题如下:

1.下面两个函数能形成重载吗?有什么问题,或者在什么情况下会出问题?

void TestFunc(int a = 10)
{
    
    
	cout << "void TestFunc(int)" << endl;
}

void TestFunc(int a)
{
    
    
	cout << "void TestFunc(int)" << endl;
}

2.C语言中为什么不能支持函数重载?
3.C++中对于函数重载底层是怎么处理的?
4.C++中能否将一个函数按照C的风格来编译?
这些问题在下一篇博文中都会详细讲解到,其实把底层讲清楚了之后,这些问题也就迎刃而解了。
注意,函数重载可不是在同一作用域下,函数名相同,参数不同这么简单哦,要想彻底明白它,前面还需铺垫一些知识,铁子们别担心,后面你会明白的。

在这里插入图片描述

遇见安然遇见你,不负代码不负卿。

猜你喜欢

转载自blog.csdn.net/weixin_57544072/article/details/124955701