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

自学网站

写在前面

一、关键字

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

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

二、命名空间

1.命名冲突

看下面一段C语言代码:

#include<stdio.h>

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

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

	return 0;
}

程序正确执行:
C++启航跟沸羊羊有关系吗?-LMLPHP
但是!如果加上stdlib.h这个头文件呢?
代码如下:

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

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

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

	return 0;
}

这个时候执行程序会报错:
C++启航跟沸羊羊有关系吗?-LMLPHP

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

#include<stdlib.h>

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

由于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.命名空间的使用

还是采用这段代码:
C++启航跟沸羊羊有关系吗?-LMLPHP
注意看上面定义的命名空间哦,瞧好了,看下面如何使用:

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++的时候最好别全部放出来,放出部分常用的即可,这点在后面你就可以体会到了。

三、C++输入&输出

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

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

  1. 使用 cout 标准输出和 cin 标准输入时,必须包含 iostream 头文件以及 std 标准命名空间;
  2. 使用C++输入输出更方便,不需要增加数据格式控制,如%d, %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.我们可以自己定义命名空间。

解题思路


练习2

下面关于C++缺省参数描述错误的是( ) 【多选】
A.缺省参数是声明或定义函数时为函数的参数指定一个默认值.
B.在调用有缺省参数的函数时,如果没有指定实参则采用该默认值,否则使用指定的实参
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的风格来编译?
这些问题在下一篇博文中都会详细讲解到,其实把底层讲清楚了之后,这些问题也就迎刃而解了。
注意,函数重载可不是在同一作用域下,函数名相同,参数不同这么简单哦,要想彻底明白它,前面还需铺垫一些知识,铁子们别担心,后面你会明白的。

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

08-26 20:36