C++关键字
C++总计63个关键字,C++兼容C语言大多数特性
关键字在这块不做详细解释仅做了解
命名空间
命名空间定义
为什么存在:解决C语言中的命名冲突的问题(C语言没有办法很好的解决这个问题)
1、我们自己定义的变量,函数可能与库里面重名冲突
2、做一些大型项目,多人协作的话,可能会有相同的命名。
命名空间的概念:在C/C++中,变量、函数和后面要学到的类都是大量存在的,这些变量、函数和类的名称将都存在于全局作用域中,可能会导致很多冲突。使用命名空间的目的是对标识符的名称进行本地化,以避免命名冲突或名字污染,namespace关键字的出现就是针对这种问题的。
关键字:namespace <空间名称>
域作用限定符: <域名·>::<要访问的名称>,域名为空的话访问全局域。
例如:
#include<stdio.h>
#include<stdlib.h> //stdlib.h 中有rand这个函数
//int rand=0;//命名冲突,报错。
//解决方法
//定义了一个ztl的命名空间
namespace ztl
{
int rand=0;
}
int a=0;
int main()
{
printf("%d\n",rand);//在这rand访问的是stdlib.h里的rand的地址,
printf("%d\n",ztl::rand);//要想访问ztl中的rand需要加上域作用限定符::。
int a=1;
printf("%d\n",a);//结果为1,访问的是main函数中的a。
printf("%d\n", ::a);//结果为0,访问的是全局域中的a。
return 0;
}
命名空间可以嵌套
namespace ztl
{
namespace n1
{
int i=0;
}
}
int main
{
ztl::n1::i=10;
printf("%d\n",ztl::n1::i);
return 0;
}
同一个工程中相同名字的命名空间编译器最后会自动合成为一个命名空间。
注意:一个命名空间就定义了一个新的作用域,命名空间中的所有内容都局限于该命名空间中
命名空间的使用
命名空间有三种使用方法:
1、直接在main函数之前展开命名空间
平时练习可以使用,但是在做项目时不推荐使用这种方法,会破坏命名空间的隔离性
using namespace std;
2、在main函数之前引用命名空间中的部分成员。
在做到命名隔离的同时,又极大的方便使用
using std::cout;
3、不展开命名空间,在使用时利用域作用限定符
可以最好的做到命名隔离,但是使用起来不方便。
std::cout<<"hello world!"<<endl;
在做一个工程时,推荐2,3结合使用。
C++输入\输出
C++标椎输入输出流:iostream
C++标椎命名空间:std
标准输出(控制台):cout
标准输入(键盘):cin
流插入:<<
注意:再使用cin和cout时必须使用包含<iostream>的头文件和使用std命名空间
使用cin/cout输出比scanf/printf方便很多,比如不用增加数据格式控制。
cout控制浮点型数据小数点后的位数太麻烦,推荐使用printf。(那个方便用哪个)
#include<iostream>
using namespace std;
int main()
{
int a=0;
cin>>a;
cout<<"hello world!"<<endl; //endl的一个作用是换行
cout<<a<<endl;
return 0;
}
缺省函数
概念
定义:缺省参数是声明或定义函数时为函数的参数指定一个默认值。在调用该函数时,如果没有指定实参则采用该默认值,否则使用指定的实参。,可以全缺省,也可以半缺省。
缺省值:如果函数没有传参的话,就使用缺省值,传参的话就使用传过来的值。简单来说就是备胎
注意:1、半缺省的话一定要从右往左给缺省值,因为传值的话是从左往右进行传值的。会导致编译出错。
2、缺省参数不能在函数声明和定义中同时出现,如果声明与定义位置同时出现,恰巧两个位置提供的值不同,那编译器就无法确定到底该用那个缺省值,推荐放在声明。
void Func(int a=0) //a为缺省值
{
cout<<a<<endl;
}
int main()
{
Func();//打印0
Func(1);//打印1;
return 0;
}
缺省函数的分类
-
全缺省函数
void TestFunc(int a = 10, int b = 20, int c = 30) { cout<<"a = "<<a<<endl; cout<<"b = "<<b<<endl; cout<<"c = "<<c<<endl; } int main() { TestFunc(1,2,3);//打印1,2,3 TestFunc(1,2);//打印1,2,30 TestFunc();//打印10,20,30 return 0; }
-
半缺省(必须从右往左缺省,并且连续缺省)
void TestFunc(int a, int b = 10, int c = 20) { cout<<"a = "<<a<<endl; cout<<"b = "<<b<<endl; cout<<"c = "<<c<<endl; } int main() { TestFunc(1,2); TestFunc(1,2,3); //TestFunc();会报错必须传一个值 return 0; }
函数重载
概念
函数重载:是函数的一种特殊情况,C++允许在同一作用域中声明几个功能类似的同名函数,这些同名函数的形参列表(参数个数 或 类型 或 顺序)必须不同,常用来处理实现功能类似数据类型不同的问题
注意:1、只是返回值类型不同的话不能构成函数重载
2、缺省值不同不回构成函数重载
3、void fun()和void fun(int a=0) 会构成重载,但是使用时会有问题,当你调用fun()时存在调用歧义,编译器不知道要调用fun(),还是fun(int a=0);
int Add(int left, int right)
{
return left+right;
}
double Add(double left, double right)
{
return left+right;
}
long Add(long left, long right)
{
return left+right;
}
int main()
{
//编译器会根据传参的类型调用不同的同名函数
Add(10, 20);//调用int add()
Add(10.0, 20.0);//调用double add()
Add(10L, 20L);//调用long add()
return 0;
}
函数重载构成的情况:
1、参数个数不同
2、 类型不同
3、 顺序不同
名字修饰(函数重载的原理)
探究:为什么C语言不支持重载,c++支持重载,又是怎么支持的?
编译的过程
fun.c 、fun.h、 test.c
1、预处理->头文件展开、宏替换、条件编译、去注释
fun.i、test.i
2、编译->检查语法、生成汇编代码
fun.s、test.s
3、汇编-> 汇编代码转换为二进制机器码
fun.o、test.o
4、链接->
a.out
C语言不支持函数重载,是因为编译的时候,两个重载函数,函数名相同,在fun.o符号表中存在歧义冲突,其次链接的时候也存在歧义和冲突,因为他们都是直接使用函数名去标识和查找,而重载函数函数名相同。
C++的目标文件符号表中不是直接用函数名来表示和查找函数。
1、函数名修饰规则,但是这个修饰规则不同的编译器下面不同。
2、有了函数名修饰规则,只要参数不同,fun.o符号表里面的重载函数就不存在二义性和冲突了
3、链接的时候,test.o的mian的函数里面去调用两个重载的函数去查找地址时,也是明确的。
在链接的过程如果在当前文件有函数的定义,name编译时就填上地址了
如果在当前文件只有函数的声明,那么定义就在去其他对的.cpp文件中,编译时没有地址,只能链接的时候去其他.o符号表中根据函数修饰名字去找,这就是链接的重要工作。
引用
引用概念:
引用不是新定义一个变量,而是给已存在变量取了一个别名,编译器不会为引用变量开辟内存空间,它和它引用的变量共用同一块内存空间。
比如:你在家的乳名。
引用的使用格式:类型& 引用变量名(对象名) = 引用实体
int main()
{
int a=0;
//引用定义
int&ab=a;
//取地址符号
printf("%p\n",&a);
printf("%p\n",&ab); //两个printf打印出来的地址相同
return 0;
}
引用在语法层,我们要理解这里没有开辟新空间,而是给a取了一个别名b。
注意:引用类型必须和引用实体是相同类型的。
引用特性
1、引用在定义时必须初始化。
int &b;//报错 int &a=10;//正确格式
2、一个变量可以有多个引用
3、一个引用一旦引用了一个实体,就不能引用其他实体了。
引用的应用
引用真正的价值体现
1、做参数(比传指针效率更高)减少拷贝
void swap(int &a,int &b)
{
int tmp=a;
a=b;
b=tmp;
}
int main()
{
int x=0,y=1;
swap(x,y);
return 0;
}
2、做返回值
所有的传值返回都会拷贝一个临时变量、引用返回不会生成拷贝返回直接返回引用
int& Add(int a,int b)
{
int c=a+b;
return c;
}
int main()
{
int ret=Add(1,2);
cout<<ret<<endl;
return 0;
}
引用和指针的区别:
-
引用在定义时必须初始化,指针没有要求
-
引用定义时必须初始化,指针最好初始化,不初始化也不会报错
-
引用在初始化时引用一个实体后,就不能再引用其他实体,而指针可以在任何时候指向任何一个同类型实体
-
没有NULL引用,但有NULL指针
-
在sizeof中含义不同:引用结果为引用类型的大小,但指针始终是地址空间所占字节个数(32位平台下占4个字节)
-
引用自加即引用的实体增加1,指针自加即指针向后偏移一个类型的大小
-
有多级指针,但是没有多级引用
-
访问实体方式不同,指针需要显式解引用,引用编译器自己处理
-
引用比指针使用起来相对更安全,有多级指针,没有多级引用。
总结:指针使用起来更复杂一些,相对指针,引用更安全一些
内联函数
概念:以inline修饰的函数叫做内联函数,编译时C++编译器会调用内联函数的地方展开,没有函数压栈的开销,提升程序运行的效率。
注意:内联函数只是一个建议,并不是有inline关键字就一定会展开,编译器会视情况而定,如果函数程序段太长,即使有inline关键字也不会展开,而是正常函数调用。
特性
-
inline是一种以空间换时间的做法,省去调用函数额开销。所以代码很长或者有循环/递归的函数不适宜使用作为内联函数。
-
inline对于编译器而言只是一个建议,编译器会自动优化,如果定义为inline的函数体内有循环/递归等等,编译器优化时会忽略掉内联。
-
inline不建议声明和定义分离,分离会导致链接错误。因为inline被展开,就没有函数地址了,链接就会找不到
拓展:宏的优缺点:
优点:
-
提高性能
-
提高代码复用率
缺点:
-
不方便调试宏(预编译阶段进行了替换)
-
导致代码可读性下降,可维护性差,容易误用。
-
没有类型安全的检查。
C++中有哪些技术可以替换宏
常数替换:const
函数替换:内联函数
auto关键字
早在C++98标准中就存在了auto关键字,那时的auto用于声明变量为自动变量,自动变量意为拥有自动的生命期。此用法是多余的,因为即使定义变量时不加"auto",变量也会有自动的生命期。用法如下:
auto int b = 20 ; //拥有自动生命期
int a =10 ; //同样拥有自动生命期
在C++11中,已经删除了此用法。
C++11引入auto类型说明符,用它能实现让编译器替我们分析表达式所属的类型。auto让编译器通过初始值来推算变量的类型。显然,auto定义的变量必须有初始值。
比如,auto Sum =a1+a2;若a1、a2是float类型,则Sum的类型就是float;若a1、a2是某个类的对象,则Sum也为该类的对象;
使用auto也能在一条语句声明多个变量,但是基本数据类型必须相同,如:
auto i=0,*p=&i; //正确,i是整数,p是整形指针
auto sz=0,pi=3.14; //错误,sz和pi类型不一致
使用规则
-
auto与指针和引用结合起来使用,用auto声明指针类型时,用auto和auto*没有任何区别,但用auto声明引用类型时则必须加&
-
在同一行定义多个变量,当在同一行声明多个变量时,这些变量必须是相同的类型,否则编译器将会报错,因为编译器实际只对第一个类型进行推导,然后用推导出来的类型定义其他变量。
不能推导的场景
-
auto不能作为函数的参数
-
auto不能直接用来声明数组