📑 目录 -一、namespace)
- 1.1 namespace的价值
- 1.2 namespace的定义
- 1.3 命名空间的使用
- 二、C++输入&输出
- 三、缺省参数
- 四、函数重载
- 五、引用
- 5.1 引用的特性
- 5.2 引用的使用
- 5.3 指针与引用的关系
- 六、const引用
- 七、inline
- 八、nullptr
一、namespace
### 1.1 namespace 的价值值
#include<stdio.h>#include<stdlib.h>intrand=10;intmain(){// 编译报错:“rand”: 重定义;因为在头文件stdlib.h中rand是“函数名”printf("%d\n",rand);return0;}在一个项目中,命名冲突是很常见的问题,而 namespace 可以很好地解决决
1.2 namespace的定义
- 定义命名空间需要用到关键字 namespace,结构为 namespace + 空间名字 + { },{ } 中为命名空间的成员,可以定义函数、变量等。。 - namespace 本质是定义一个域,这个域跟全局域各自独立,即不同的域可以定义同名变量。
- namespace 只能定义在全局域,但可以嵌套定义。
#include<stdio.h>#include<stdlib.h>namespaceTian{intrand=10;//命名空间可以嵌套namespaceLi{intrand=20;};namespaceNuo{intrand=30;};}intmain(){// 打印标准库rand函数的地址(函数要加&)printf("%p\n",&rand);//这里访问指定命名空间的randprintf("%d\n",Tian::rand);//10printf("%d\n",Tian::Li::rand);//20printf("%d\n",Tian::Nuo::rand);//30}- 项目中多文件中同名 namespace,被认为是一个 namespace,不会冲突。突 - C++ 标准库都放在一个叫 std(standard)的命名空间中。。
1.3 命名空间的使用
编译查找一个变量的声明/定义时,默认只会在局部或者全局查找,不会到命名空间里面去查找。可以通过以下三种方式去访问:问
#include<stdio.h>namespaceTian{inta=10;}intmain{//编译报错 a:未声明标识符printf("%d\n",a);//指定命名空间访问printf("%d\n",Tian::a);// using 将命名空间中某个成员展开,项目中经常访问的不存在冲突的成员推荐这种方式式usingTian::a;printf("%d\n",a);// 展开命名空间中全部成员(冲突风险很大))usingnamespaceTian;printf("%d\n",a);return0;}## 二、C++ 输入 & 输出出
<iostream>是 Input Output Stream 的缩写,是标准的输入、输出流库,定义了标准的输入、输出对象。。-cout、cin、endl等都属于 C++ 标准库,C++ 标准库都放在一个叫std(standard)的命名空间中,所以要通过命名空间的使用方式去用它们。。 -std::cin是istream类的对象,它主要面向窄字符(narrow characters (of type char))的标准输入流。。-std::cout是ostream类的对象,它主要面向窄字符的标准输出流。。- std::endl 是一个函数,流插⼊输出时,相当于插入⼀个换行字符加刷新缓冲区。
- <<是流插入运算符,>>是流提取运算符。(C语言还用这两个运算符做位运算左移/右移)。
- 使用C++输入输出更方便,不需要像printf/scanf输入输出时那样,需要手动指定格式,C++的输入
输出可以自动识别变量类型,C++的流能更好的支持自定义类型对象的输入输出。 - ⼀般日常练习中我们可以using namespace std,实际项目开发中不建议using namespace std
#include<iostream>usingnamespacestd;intmain(){inta=0;doubleb=0.1;charc='x';cout<<a<<" "<<b<<" "<<c<<endl;// 可以⾃动识别变量的类型cin>>a;cin>>b>>c;cout<<a<<endl;cout<<b<<" "<<c<<endl;return0;}
在io需求比较高的地方,如部分大量输入的竞赛题中,加上以下3行代码可以提高C++IO效率
ios_base::sync_with_stdio(false);cin.tie(nullptr);cout.tie(nullptr);三、缺省参数- 缺省参数是声明或定义函数时为函数的参数指定一个缺省值。在调用该函数时,如果没有指定实参则采用该形参的缺省值,否则使用指定的实参,缺省参数分为全缺省和半缺省参数。数- 全缺省就是全部形参给缺省值,半缺省就是部分形参给缺省值。C++ 规定半缺省参数必须从右往左依次连续缺省,不能间隔跳跃给缺省值。。- 带缺省参数的函数调用,C++ 规定必须从左到右依次给实参,不能跳跃给实参。。
- 做项目时函数声明和定义分离时,缺省参数不能在函数声明和定义中同时出现,规定必须函数声明给缺省值。
缺参使用示例*
#include<iostream>usingnamespacestd;voidFunc(inta=0){cout<<a<<endl;}intmain(){// 没有传参时,使用参数的默认值值Func();//输出0//传参时,使⽤指定的实参Func(10);//输出10return0;}#include<iostream>usingnamespacestd;// 全缺省voidFunc1(inta=10,intb=20,intc=30){cout<<"a = "<<a<<endl;cout<<"b = "<<b<<endl;cout<<"c = "<<c<<endl<<endl;}// 半缺省voidFunc2(inta,intb=10,intc=20){cout<<"a = "<<a<<endl;cout<<"b = "<<b<<endl;cout<<"c = "<<c<<endl<<endl;}intmain{Func1();//10+20+30 = 60Func1(1);//1+20+30 = 51Func1(1,2);//1+2+30 = 33Func1(1,2,3);//1+2+3 = 6Func2(100);//100+10+20 = 130Func2(100,200);//100+200+20 = 320return0;}四、函数重载
C++ 支持在同一作用域中出现同名函数,但是要求这些同名函数的形参不同(参数个数不同或者类型不同)。这样 C++ 函数调用就表现出了多态行为,使用更灵活。。
#include<iostream>// 参数类型不同usingnamespacestd;intAdd(intleft,intright){count<<"int Add(int left, int right)"<<endl;returnleft+right;}doubleAdd(doubleleft,doubleright){cout<<"double Add(double left, double right)"<<endl;returnleft+right;}//参数个数不同voidf(){cout<<"f()"<<endl;}voidf(inta){cout<<"f(int a)"<<endl;}// 参数类型顺序不同voidf(inta,charb){cout<<"f(int a,char b)"<<endl;}voidf(charb,inta){cout<<"f(char b, int a)"<<endl;}// 返回值不同不能作为重载条件,因为调⽤时也⽆法区分//void fxx()//{}////int fxx()//{//return0;//}// 下⾯两个函数构成重载// 但是f1()调⽤时,会报错,存在歧义,既可以匹配无参的f1(),也能匹配带默认参数的f1(int a=10)(//void f1(){// cout << "f1()" << endl;//}//void f1(int a = 10){// cout << "f1(int a)" << endl;//}五、引用
引⽤不是新定义⼀个变量,而是给已存在变量取了一个别名,编译器不会为引⽤变量开辟内存空间,它和它引⽤的变量共⽤同⼀块内存空间比如:李逵,江湖上人称 “黑旋风”;林冲,外号豹子头头
类型& 引用别名 = 引用对象;*
intmain(){inta=0;// b、c 都是 a 的别名名int&b=a;int&c=b;// 也可以给别名 b 取别名,d 相当于还是 a 的别名名int&d=b;++d;// a+++return0;}5.1引用的特性
- 引用在定义时必须初始化
- 引用绑定的对象不能换
inta=10;int&b=a;intc=20;b=c;// 这里并非让 b 引用 c,因为 C++ 引用不能改变指向,这里是一个赋值cout<<&a<<endl;cout<<a<<endl;cout<<&b<<endl;cout<<&c<<endl;b、c 引用的对象地址一样,即绑定对象一样
5.2 引用的使用
引用在实践中主要用于引用传参和引用做返回值中减少拷贝提高效率和改变引用对象时同时改变被引用对象。。
用引用来实现交换函数,可以减少地址拷贝,语法更简洁
voidSwap(int&rx,int&ry){inttmp=rx;rx=ry;ry=tmp;}这里 ret 是函数局部变量,函数运行结束后会销毁。。return ret 时:会把 ret 的值拷贝一份到临时变量,放到寄存器里,再把临时变量的值赋值给外面的 x。
缺点:产生拷贝,大对象时开销大
intfunc(){intret=0;returnret;}intx=func();由此可以明白顺序表找下标的方法,是将数组对应下标的值拷贝到临时变量里返回,同时也无法修改这个临时变量的值,因为这个临时变量是纯右值,语法规定右值不能作为赋值左操作数*
intSLAt(SL&sl,inti){returnsl.a[i];// 拷贝元素到临时变量,产生副本}SLAt(sl,i)+=1;// 这个编译会报错,左操作数要是左值值修改后,return sl. a[i],可以理解为用一个临时引用变量 int& tmp = sl. a[i](编译器不会真的创建一个名叫 tmp 的变量,只是逻辑等价),返回的是返回对象的别名,这样就可以对数组的数进行修改
int&SLAt(SL&sl,inti){returnsl.a[i];}SLAt(sl,i)+=1;注意:func()那里不能把返回值int变成int&,因为ret是函数局部变量,返回他的引用时,ret空间已经被销毁了,本质是一种类似野指针的行为
5.3 指针与引用的关系
C++ 中指针和引用在实践中相辅相成,功能有重叠性,但是各有自己的特点,互相不可替代。。
- 语法概念上:引用是一个变量的别名,不开空间;而指针是存储一个变量地址,要开空间。。- 引用在定义时必须初始化,指针建议初始化,但是语法上不是必须的。。- 引用在初始化时绑定一个对象后,就不能再绑定其他的对象了;而指针可以不断地改变指向对象。。- 引用可以直接访问指向对象,指针需要解引用才是访问指向对象。。
- 指针容易出现空指针或者野指针,而引用一般不会,因此引用使用起来更安全一些。
- sizeof(引用):得到的是原数据类型本身的大小,但sizeof(指针)始终是地址空间所占字节个数(32位平台下占4byte,64位下是8byte)。
六、 const引用
- 在 C++ 中对象的访问权限在引用过程中可以缩小,但是不能放大大
constinta=10;// a是常量,只读,不能修改int&r1=a;// 报错!普通引用会修改权限,权限放大,禁止constint&r2=a;// 合法,const引用只读,权限匹配intb=20;// b既可读,也可写constint&r=b;// 合法,权限可以缩小r=100;// 报错:r是const引用,只读b=100;// 合法:原变量本身依旧能改int& rb = a*3;double d = 12.34;int& rd = d;a*3的结果保存在一个临时对象中,int& rd = d也是类似,在类型转换中会产生临时对象存储中间值,也就是说 rb 和 rd 引用的都是临时对象,而 C++ 规定临时对象具有常性,所以这里就触发了权限放大,必须要用常引用才可以。。- 所谓临时对象就是编译器需要一个空间暂存表达式的求值结果时临时创建的一个未命名的对象,C++ 中把这个未命名对象叫做临时对象象
inta=10;constint&ra=30;// int& rb = a * 3; 编译报错,a*3 的运算结果会用一个临时对象储存,不能用可修改普通引用 int& 绑定,因为权限不能放大大const int& rb = a*3; // const int& 常引用可以正常绑定定doubled=12.34;// int& rd = d;编译出错,因为d 是 double 类型变量,引用 rd 是 int&,类型不匹配,编译器会自动隐式转换:把 12.34 截断小数,生成临时 int 值 12,这个 12 是匿名临时对象constint&rd=d;## 七、 inlinee
- 运行机制:使用 inline 修饰的函数称为内联函数:编译阶段编译器会将函数代码原地展开,省去函数调用时栈帧创建、参数压栈、跳转返回的开销,提升程序执行效率。
- 编译器特性:仅为建议;inline 只是给编译器的优化提示,而非强制指令,编译器有权忽略展开请求,以下场景会直接放弃内联:
- 函数存在递归调用。
- 函数代码量大、逻辑复杂。
- 函数包含循环、分支多的复杂逻辑。
- C++ 引入 inline 的核心目的:安全替代 C 语言宏函数,兼顾代码展开效率与类型安全、可调试性。
- 声明和定义不能分离到两个文件,声明时同时写出定义。
八、 nullptr
NULL 的底层定义:
- C 语言:NULL 是无类型空指针 (void*)0
- C++:NULL 直接等价于整数字面量 0### nullptr 核心特性性
- 是关键字,不是宏宏- 只能隐式转换成任意指针类型,不能转换成整数数