需要云服务器等云产品来学习Linux的同学可以移步/-->腾讯云<--/-->阿里云<--/-->华为云<--/官网,轻量型云服务器低至112元/年,新用户首次下单享超低折扣。
目录
一、命名空间
C语言是没办法解决命名冲突问题的。变量、函数和类名在一个工程中往往被大量定义,那么在协作开发中,为了防止不同的小组间命名的冲突,c++引入了namespace关键字,使标识符的名称进行本地化, 以避免命名冲突或名字污染。
1、作用域限定符::
当a的前面加上域作用限定符::,a只能访问全局域,如果全局中a不存在,不会再去局部域中找,会直接报错
二、命名空间namespace
1、命名空间的嵌套
namespace jly
{
int scanf=0;
int Add(int a, int b)
{
return a + b;
}
namespace bjb//命名空间嵌套命名空间
{
struct std
{
int age;
char name[10];
};
}
}
int main()
{
struct jly::bjb::std student={10,"jk"};//嵌套结构体的调用
return 0;
}
命名空间是可以嵌套命名空间的。调用时,需要一步一步指定命名空间域。如果命名空间中找不到对应变量名,将会报错。
2、相同命名空间会被合并
在同一个工程中,允许存在多个名字相同的命名空间,编译器最终会将同名命名空间里的内容进行合并。
3、命名空间的全部展开与部分展开
using namespace std;//全部展开
using std::cout;//部分展开
std是C++官方库定义的命名空间(像cout啥的就在std里面,但是c语言如printf就不在std中)
全部展开用起来方便,但命名空间就失去了命名隔离的作用了。写工程不采用,日常写小代码可以展开。
三、C++中的输入输出
int main()
{
std::cout << "hello world" << std::endl;//<<流插入运算符
int a = 0;
std::cin >> a;//>>流提取运算符
}
1. cout和cin是全局的流对象,endl是特殊的C++符号,表示换行输出,使用cout标准输出对象(控制台)和cin标准输入对象(键盘)时,他们都被包含在<iostream>头文件中一个名为std的命名空间中,所以我们写C++代码#include <iostream>是为了包含C++的官方库,using namespace std是为了展开<iostream>中的std命名空间。
2. <<是流插入运算符,>>是流提取运算符
四、缺省参数
缺省参数是声明或定义函数时为函数的参数指定一个缺省值。在调用该函数时,如果没有指定实参则采用该形参的缺省值,否则使用指定的实参。
缺省值只能是常量或全局变量。
1、全缺省参数
void func(int a = 5, int b = 6, int c = 9)
{
cout << a<< b << c << endl;
}
int main()
{
func(1,2,3);//a接收1,b接收2,c接收3
func(1,2);//a接收1,b接收2,c使用缺省值9
fun();//a使用缺省值5,b使用缺省值6,c使用缺省值9
return 0;
}
全缺省参数在调用时,不给参数时,将会使用缺省值调用。给参数时,实参和形参从左到右匹配。
2、半缺省参数
void func(int a , int b = 6, int c = 9)
{
cout << a << b << c << endl;
}
int main()
{
func(1, 2, 3);//a接收1,b接收2,c接收3
func(1);//a接收1,b使用缺省值6,c使用缺省值9
func();//错误写法,因为形参a不是缺省,至少传一个参数
return 0;
}
未使用缺省值的形参,在传参时必须传入,有缺省值的形参,根据需要可传可不传。
1、缺省必须从右往左缺省,不能a变量给了缺省值,b没给,c给了,这种跳跃的缺省语法是不被允许的。
2、缺省参数不能在函数声明和定义中同时出现,当工程中有.h和.c时,在.h(声明)中给缺省值,定义中就不要给缺省值了!
五、函数重载
函数重载是函数的一种特殊情况,C++允许在同一作用域中声明多个同名函数,这些同名函数参数类型、参数个数、类型的顺序不同,常用来处理实现功能类似数据类型不同的问题。
1、参数类型不同
void Swap(int* a, int* b)
{
;
}
void Swap(double* a, double* b)
{
;
}
2、参数个数不同
void f()
{
cout << 10 << endl;
}
void f(int a, int b)
{
cout<<20<<endl;
}
3、类型顺序不同
void f(char b, int a)
{
cout << 10 << endl;
}
void f(int a, char b)
{
cout<<20<<endl;
}
注意,如果这里的两个参数的类型全是int,那么变量a和b交换位置,是不构成函数重载的。
4、函数重载的原理
为什么C语言不支持函数重载而C++支持呢?
在linux下,采用gcc编译完成后,函数名字的修饰没有发生改变。
而在g++的函数修饰后变成【_Z+函数长度 +函数名+类型首字母】。 在linux下,采用g++编译完成后,函数名字的修饰发生改变,编译器将函数参数类型信息添加到修改后的名字中,所以就能根据形参,来区分同名函数。
C语言没办法支持重载,因为同名函数没办法区分。而C++是通过函数名修饰规则来区分,只要参数不同,修饰出来的名字就不一样,就支持了重载。
5、为什么不能根据函数的返回值进行函数重载
错误的写法:
void f(int a, int b)
{
cout << 10 << endl;
}
int f(int a, int b)
{
cout<<20<<endl;
}
原因:根据函数重载的原理,函数名修饰规则只和形参有关,与返回值无关。
那么函数名修饰规则为什么不加上返回类型的区分用于函数重载?因为就算函数名修饰规则中有函数返回类型的区分,我们在调用函数传参的时候,编译器是没办法知道我们想要调用哪个函数。比如上方代码,我们调用f(1, 2)时,编译器无法区分我们调用哪个f()函数,所以不能根据函数的返回值进行函数重载。
六、引用
引用,就是已定义变量的一个别名。
可以给变量取别名,也可以给变量的别名取别名,编译器不会为引用额外开辟空间,他们都代表着同一块空间。
取的别名的类型必须与原变量保持一致。
1、引用特性
1.1引用在定义的时候必须初始化
1.2一个变量可以有多个引用
略
1.3一旦成为了某个变量的引用,引用将一直效忠这个变量
这里的代码中,ra是a的引用,那么ra就和a永久绑定了。ra=b是让b的值赋给ra,ra被修改成5,a也就变成了5。
2、常引用(指针和引用的赋值,权限可以缩小不能扩大)
2.1对常量的引用
int main()
{
const int& a = 10;
return 0;
}
因为10是常量,有“常属性”,可读不可写,如果不加const修饰,那么a的权限就变成了可读可写,这是权限的放大,语法是不通过的,所以这里的a必须加上const修饰。
2.2对const常量的引用
int main()
{
const int a = 10;
const int& ra = a;
return 0;
}
因为a有const修饰,所以a的引用也必须有const修饰,做到权限平移。
2.3隐式、强制类型转换的引用
int main()
{
double a = 10.256;
const int& ra = a;//ra打印结果是10
return 0;
}
这里将发生隐式类型转换,a将自身的数值截断后复制到一个tmp临时变量中,ra对tmp这个临时变量进行引用。临时变量具有常属性。所以这里要加const修饰。
2.4传参时的权限问题
1、ra是只读的,传参时,权限不能扩大为可读可写,这里时一个权限放大的错误。
我们在传参时,如果这个形参在函数中不会被修改,那我们可以无脑用const来修饰形参。
2、缺省引用,必须使用const修饰
3、const提升生命周期
Add函数的的返回值会先拷贝一份一模一样副本作为临时变量用于带回返回值,临时变量具有常属性,所以这里用const修饰,const修饰后,该临时变量的生命周期延长,编译器不会报错。
3、引用的使用场景
3.1引用做参数
void Swap(int& a, int& b)
{
int tmp=a;
a = b;
b = a;
}
引用作为函数的形参,函数将使用原始数据,而不是原始数据的临时拷贝。
引用做参数的优势
1、减少拷贝,提高效率
2、作为输出型参数,在函数中,形参改变会改变实参。
3、一般引用做参数的(函数中形参不需要改变)都要用const引用
3.2引用做返回值
错误用法:
int& Add(int a, int b)
{
int sum = a + b;
return sum;
}
int main()
{
int ret = Add(1, 2);
cout << ret << endl;
return 0;
}
当Add函数调用完毕后,函数栈帧被销毁,同时通过临时变量将sum的别名返回,但是sum它已经被销毁了。所以这是一种错误的用法。
正确用法:
int& Add(int a, int b)
{
static int sum = a + b;
return sum;
}
int main()
{
int ret = Add(1, 2);
cout << ret << endl;
return 0;
}
函数调用完毕后,栈帧销毁,同时通过临时变量将sum的别名返回,由于sum是静态的,所以不会出现上方例子的情况。
总结:函数栈帧结束后,如果返回值不会随着栈帧的结束而销毁,那么就可以使用引用返回。
引用返回相较于传值返回的优势
1、减少拷贝,提高运行效率
如图所示,传值返回是会生成一份返回值的临时拷贝的,这将会消耗空间同时降低运行效率。
而传引用返回也会生成一个临时变量,这个临时变量是sum的别名,它和sum共同表示同一块内容,传引用返回不会占用额外空间用于拷贝,提升运行效率。
2、可以直接对返回值进行修改
因为modify函数返回的是pos节点的引用,我们可以直接对这个引用进行修改操作。
3.3引用和指针的区别
1、语法上引用是变量的别名,不开空间,而指针存储一个变量地址。但是引用的底层是用指针实现的。
2、引用在定义时必须初始化,指针没有要求
3、引用在初始化时引用一个实体后,就不能再引用其他实体,而指针可以在任何时候指向任何一个同类型实体 。这就导致了C++的引用代替了一部分指针的功能,但不能完全代替。例如链表中的next必须用指针实现。如果采用引用的方式引用节点,插入删除节点要修改next,没办法通过引用改变next指向的值。
4、没有NULL引用,但有NULL指针
5、在sizeof中含义不同:引用结果为引用类型的大小,但指针始终是地址空间所占字节个数(32
位平台下占4个字节)
6、引用自加即引用的实体增加1,指针自加即指针向后偏移一个类型的大小
7、有多级指针,但是没有多级引用
8、访问实体方式不同,指针需要显式解引用,引用编译器自己处理
9、引用比指针使用起来相对更安全
七、inline关键字
1、inline的概念
以inline修饰的函数是内联函数,编译时C++编译器会在调用内联函数的地方展开,没有函数调用建立函数栈帧的开销,能够提升程序运行的效率。一般用于语句简短的小函数当中。
2、inline修饰的函数的特点
1、inline修饰函数是一种以空间(编译出来的可执行程序的大小)换时间的方法。缺点时会使可执行程序变大,优点是没有函数调用建立函数栈帧的开销,能够提升程序运行的效率。
2、程序员用可以inline修饰函数,但是否让这个函数成为内联函数展开,由编译器决定。
3、内联用于修饰规模较小、非递归、频繁调用的函数。
4、内联函数声明和定义不要分离。一旦分离,编译器认为内联函数无需调用,程序走到编译这步时,内联函数的定义不会被汇总符号,程序再走到汇编阶段,单个文件进行符号表的汇总(这时函数定义文件没有该函数的符号,而声明文件中有声明的符号),程序再走到链接这一步,进行符号表的合并,编译器发现只有函数声明的符号,却找不到函数定义的符号,自然形成了链接错误。
3、宏的优缺点和替代
宏的优点:增强代码的复用性,执行速度比函数快
宏的缺点:无法调试,宏可读性差且难写,没有类型的安全检查
宏的替代:用const、enum定义常量,用inline修饰短小函数。
八、auto的用法
auto用于自动推导类型
注意auto不能做函数参数,因为类型不明。auto不能声明数组。
九、范围for
范围for用于遍历数组,依次取arr数组中的数据赋值给e,自动迭代到结束。
int main()
{
int arr[] = { 1,2,3,4,5,6 };
//遍历数组
for (auto e : arr)
{
cout << e << " ";
}
//遍历并修改数组
for (auto& e : arr)
{
cout << e * 2 << " ";
}
return 0;
}
注意范围for循环迭代的范围必须是确定的:
void func(int array[])
{
for(auto& e : array)
cout<< e <<endl;
}
函数传参,传进来的不是数组是指针,for循环迭代范围不明。
十、关键字nullptr
在C++中NULL被定义为常量0,而不是指针类型。
#ifndef NULL
#ifdef __cplusplus
#define NULL 0
#else
#define NULL ((void *)0)
#endif
#endif
这样的话,传参可能会出现错误。
1. 在使用nullptr表示指针空值时,不需要包含头文件,因为nullptr是C++11作为新关键字引入 的。
2. 在C++11中,sizeof(nullptr) 与 sizeof((void*)0)所占的字节数相同。
3. 为了提高代码的健壮性,在后续表示指针空值时建议最好使用nullptr。
优快云话题挑战赛第2期
参赛话题:学习笔记