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;
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
程序正确执行:
但是!如果加上stdlib.h这个头文件呢?
代码如下:
#include<stdio.h>
#include<stdlib.h>
int rand = 0;//定义一个全局变量rand
int main()
{
printf("%d\n", rand);
return 0;
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
这个时候执行程序会报错:
看错误列表怎么说:"rand"重定义,以前的定义是函数
这什么情况呀,为什么一开始还好好的,加上stdlib.h头文件之后就报错了呢?毋庸置疑的是肯定跟 stdlib.h 头文件有关系,但是具体有什么关系还得继续往下看:
#include<stdlib.h>
- 1
之所以加上这行代码之后报错,是因为在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;
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
这样程序就正确执行了,可能大家并不理解为什么这样程序可以跑起来,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;
};
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
· 嵌套命名空间
namespace byte
{
int a = 0;
//命名空间可以嵌套,一般多是2~3层
namespace data
{
struct Node
{
int val;
struct Node* next;
};
}
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
· 同名命名空间
同一个工程中允许存在多个相同名称的命名空间,编译器最后会合并在同一个命名空间中。
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;
};
}
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
注意事项:
- 一个命名空间就定义了一个新的作用域,命名空间中的内容都局限于其中;
- 命名空间不会影响变量的生命周期,而且命名空间的定义只能是全局的,不能在局部中定义命名空间,比如在main函数中定义就是错误的;
- 一般命名空间是定义在头文件下的。
对于命名空间的定义做一个总结:
1. 命名空间中可以定义变量、函数以及类型;
2. 命名空间支持嵌套,防止工程太大导致命名冲突;
3. 同名的命名空间是可以同时存在的,编译器编译时会自动合并。
4.命名空间的使用
还是采用这段代码:
注意看上面定义的命名空间哦,瞧好了,看下面如何使用:
using namespace byte;
//意思是把byte这个命名空间展开,将其中内容全部放出来
//但是需要注意的是嵌套的命名空间中的内容并没有放出来哦
- 1
- 2
- 3
using namespace byte;
using namespace data;
//这两行代码的意思是:先展开byte,再展开data
//如果颠倒这两行代码的顺序是不可以的,而且其意义不等同于using namespace byte::data;
- 1
- 2
- 3
- 4
using namespace byte::cache;
//注意哦,这里展开的是byte中的cache,并没有将byte展开
- 1
- 2
using的意义:
|
比如:
全部放出来:
using namespace byte;//将byte展开
- 1
部分放出来:
using namespace byte::a;//将byte中的变量a放出来
- 1
注意哦,只有放出来才能使用哦,否则还局限于命名空间中。
命名空间的三种使用方式:
· 加命名空间名称及作用域限定符
printf("%d\n", byte::a);
//这样就可以直接打印byte中的a
- 1
- 2
· 使用using将命名空间中成员引入
通常都是把部分常用的放出来。
using byte::a;//将byte中的变量a放出来
int main()
{
printf("%d\n", a);
}
- 1
- 2
- 3
- 4
- 5
- 6
· 使用using namespace命名空间引入
这种使用方式直接将命名空间中的内容全部放了出来。
using namespace byte;
int main()
{
printf("%d\n", a);
printf("%d\n", b);
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
对于第3种使用方式,我们最常用的就是:
using namespace std;
- 1
这里首先跟大家说一下,这样的使用方式是存在风险的,为什么这样说呢,下面补充一条知识点:
std 是C++标准库的命名空间,将它里面的内容全部放出来虽然方便我们使用了,但是存在冲突风险,所以我们在写一些C++ IO 型题目不在乎冲突的时候可以全部放出来,平时学习C++的时候最好别全部放出来,放出部分常用的即可,这点在后面你就可以体会到了。
|
三、C++输入&输出
我们在学习C语言的时候都是这样向世界问好的:
那么C++是如何向世界打招呼的呢?
其中 ‘endl’ 相当于 ‘\n’,看到了吧,这里我并没有将 std 中的内容全部放出来,而是只把常用的cout 和 endl 放了出来。
对C++的输入&输出进行说明:
- 使用 cout 标准输出和 cin 标准输入时,必须包含 iostream 头文件以及 std 标准命名空间;
- 使用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;
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
生动来说的话就是:
TestFunc();//没有传参时,使用参数的默认值
//喜羊羊不在,沸羊羊登场(默认参数)
- 1
- 2
TestFunc(20);//传参时,使用指定的实参
//喜羊羊回来了,没有沸羊羊什么事了
- 1
- 2
总结:
|
2.缺省参数的分类
· 全缺省参数
全缺省参数就是参数中全部给了缺省值。比如下面的函数:
void TestFunc(int a = 10, int b = 20, int c = 30)
{
cout << "a = " << a << endl;
cout << "c = " << c << endl << endl;
}
- 1
- 2
- 3
- 4
- 5
全缺省很简单,就不过多赘述了。
· 半缺省参数
注意哦,半缺省参数可不是参数中一半给了缺省值,哈哈,而是部分参数给了缺省值。
还有一个注意事项是:半缺省的必须从右往左缺省,中间不能有间隔,这是语法规定的。
比如下面这段代码是没有问题的:
void TestFunc(int a, int b = 20, int c = 30)
{
cout << "a = " << a << endl;
cout << "b = " << b << endl;
cout << "c = " << c << endl;
}
- 1
- 2
- 3
- 4
- 5
- 6
但是不允许出现下面这样的写法:
void TestFunc(int a = 10, int b = 20, int c)
{
……
}
- 1
- 2
- 3
- 4
这样也不可以:
void TestFunc(int a = 10, int b, int c = 30)
{
……
}
- 1
- 2
- 3
- 4
所以,这个时候你应该理解上面我所说的注意事项了吧。
再次强调一遍:
|
还有一个需要注意的地方:
缺省参数不能在函数的声明和定义的时候同时出现,只在函数声明的时候参数带有缺省值,函数定义的时候不能再重复出现了。(针对于函数的声明和定义分开写的情况,只有函数定义时可忽略)
为什么这样说呢,请看下面一段代码:
//Func.h
//函数的声明
void TestFunc(int a = 10);
- 1
- 2
- 3
//Func.cpp
//函数的定义
void TestFunc(int a);
- 1
- 2
- 3
为什么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;
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
- 31
- 32
- 33
- 34
- 35
- 36
- 37
- 38
- 39
- 40
- 41
- 42
- 43
- 44
- 45
但是上面的代码存在问题,每次入栈都需要判断栈是否满了,而且扩容是需要付出性能等方面的代价的。
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;
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
当我们知道要开辟多大空间时,使用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;
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
2.C语言中为什么不能支持函数重载?
3.C++中对于函数重载底层是怎么处理的?
4.C++中能否将一个函数按照C的风格来编译?
这些问题在下一篇博文中都会详细讲解到,其实把底层讲清楚了之后,这些问题也就迎刃而解了。
注意,函数重载可不是在同一作用域下,函数名相同,参数不同这么简单哦,要想彻底明白它,前面还需铺垫一些知识,铁子们别担心,后面你会明白的。
|
文章来源: bit-runout.blog.csdn.net,作者:安然无虞,版权归原作者所有,如需转载,请联系作者。
原文链接:bit-runout.blog.csdn.net/article/details/124955701
- 点赞
- 收藏
- 关注作者
评论(0)