目录
一.命名空间和输入输出流
1.命名空间
a.命名空间的由来
b.命名空间的使用
①命名空间的定义
②命名空间的展开
③域作用访问限定符
2.输入输出流
a.基本概念
b.标准输入输出流
1.缺省参数
a.全缺省
b.半缺省
2.函数重载
函数名修饰规则
三.引用和范围for
1.引用
a.引用的定义和初始化
b.引用的使用场景
c.引用和指针的区别
auto%E5%92%8C%E8%8C%83%E5%9B%B4for-toc" style="margin-left:40px;">2.auto和范围for
auto%E5%85%B3%E9%94%AE%E8%AF%8D-toc" style="margin-left:80px;">a.auto关键词
b.范围for
一.命名空间和输入输出流
1.命名空间
a.命名空间的由来
在C语言中,当代码量过多时,就可能产生命名冲突问题,如:与库中的函数名冲突、与他人文件中全局变量、函数名称冲突。这种名称冲突防不胜防,而且修改起来颇为麻烦,所以,为了解决这一麻烦,我们在C++中引入命名空间。
那么,什么是命名空间?
命名空间是一种封装标识符(如变量名、函数名、类名等)的空间域,我们可以在命名空间域内定义变量、函数或类等,使用时通过使用域作用访问限定符来避免命名冲突问题,它还可以帮助组织代码,使其更加模块化和易于维护。
b.命名空间的使用
①命名空间的定义
namespace myspace{} —— 定义一个命名空间域,域名为myspace.
②命名空间的展开
using namespace bit; —— 将名为bit的命名空间域展开
③域作用访问限定符
访问格式 —— 域名 + “ : : ” + 目标名
name :: a 域作用限定符,若左边有域名,则去左边的命名空间域内访问该变量;若左边不写域名,则默认访问的是全局域。
在一个函数内部,对某一变量的访问优先级是:访问指定的命名空间域——>局部域——>全局域和展开了的命名空间域
展开命名空间并不是将命名空间里的数据拷贝到当前,而是决定了编译器编译时是否去展开的命名搜索,展开了的命名空间域内的变量拥有全局属性
命名空间的特性:
a.命名空间内可以定义变量、函数、类型;
b.命名空间可以嵌套;
c.在同一个文件中,可以存在多个域名相同的命名空间,它们在编译时会被整合到一起。
2.输入输出流
a.基本概念
流:在C++中,数据像流水一样从一个地方流动到另一个地方,因此将输入输出称为“流”。输入输出流是指由若干字节组成的字节序列,这些字节中的数据按顺序从一个对象传送到另一对象。
输入流:将输入的字符序列形式的数据变换成计算机内部形式的数据(二进制或ASCII)后,再赋给变量。输入操作时,字节流从输入设备(如键盘、磁盘)流向内存。
输出流:将要输出的数据变换成字符串形式后,送到输出流(文件)中。输出操作时,字节流从内存流向输出设备(如屏幕、打印机、磁盘等)。
b.标准输入输出流
cin:标准输入流对象,用于从标准输入设备(通常是键盘)读取数据。使用 >>
操作符从输入中提取数据到变量。
cout:标准输出流对象,用于输出数据到标准输出设备(通常是显示器)。使用 <<
操作符连接输出项,endl
用于输出换行。
cerr:标准错误输出流对象,通常用于输出错误信息。与显示器关联,但缓冲区满或遇到endl
时才向显示器输出,且不能被重定向。
<< —— 流插入运算符; >> —— 流提取运算符;
举例:
std :: cin >> str ; 本质是从键盘读取我们输入的数据,然后将其写到 str 变量中保存。
std :: cout << "hello world" << std :: endl ; 本质是将字符串打印到屏幕上。
二.缺省参数和函数重载
1.缺省参数
缺省参数是指在声明或定义函数时,为函数的某些或全部参数指定一个默认值。在调用该函数时,如果没有为这些参数提供实参,则采用这些参数的缺省值;如果提供了实参,则使用指定的实参。
a.全缺省
函数的所有参数都设定一个缺省值,如:
void func(int a=10, int b=20, int c=30) { }
func(1,2);
注意:调用函数时,所传参数按从左到右给形参赋值!
b.半缺省
半缺省参数必须从右往左依次给出,不能间隔着给,如:
void func(int a, int b=10, int c=20);
func(1,2);
调用函数时,所传参数按从左到右赋值给形参~~
缺省值必须是常量或者全局变量。
注意:函数的声明和定义不能同时给缺省值,且在VS中,只能是函数的声明给缺省值,函数的定义不能给缺省值,否则编译器会报错!!
2.函数重载
C++允许在同一作用域中声明几个功能类似的同名函数,这些同名函数的形参列表(参数个数、类型或类型顺序,注:返回值不算)不同,常用来处理实现功能类似而数据类型不同的问题。
注:void func(); 和 void func(int a=10); 二者构成函数重载,但调用func()时,编译器依旧会报错,因为该函数的调用不明确。
函数名修饰规则
*C语言为何无法实现函数重载,C++又是如何支持函数重载的??--- 编译链接过程 + 函数名修饰规则!
在Linux系统下:
在gcc编译器(编译C语言代码)编译的过程中,编译器为函数生成地址时,只认函数名作唯一的判断标准;
在g++编译器(编译C++代码)编译的过程中,编译器在为函数生成地址(汇编过程,符号表汇总)时,有独特的函数名修饰规则,即用前缀+函数名长度+函数名+参数类型+...等等 为判断标准。
如:对void func(int a)和void func(int a, double b) 编译生成的函数标识分别是 <_Z4funci> 和 <_Z4funcid>,其中:_Z是前缀,4是函数名长度,func是函数名,i和id是参数类型。
为什么无法以返回值类型用作函数重载的判断标准?--- 函数调用歧义问题~~
如:int func(); double func(); int main() { func(); }
这种情况下,编译器就无法判断到底调用哪个 func() 函数,出现函数调用歧义问题!
小知识:在Linux命令行中:objdump -S test.exe 指令 可以查看test.exe形成时的汇编过程
三.引用和范围for
1.引用
引用,其实就是给一个已存在的变量取了个别名,通过引用,我们可以直接操作原始数据,而无需进行数据的拷贝。引用在函数参数传递和返回值时,可以大大减少数据拷贝所带来的开销~~
a.引用的定义和初始化
& 我们可以在变量类型后添加 & 符号来进行引用对象的定义与声明。例如,int& ref = var;表示 ref
是 var 的引用。
注意:引用在创建时必须被初始化,且一旦初始化后,就不能再改变引用的目标。这是因为引用在底层实现上通常是一个指针常量,指向其目标变量。
通过引用,我们可以直接修改目标变量的值。这种特性使得引用在函数参数传递时非常有用,因为它允许函数直接修改调用者提供的变量。
引用的大小:在大多数实现中,引用的大小与其所引用的对象类型无关,且通常与指针的大小相同。这是因为引用在底层通常是通过指针来实现的。
b.引用的使用场景
①引用传参(输出型参数,面对大对象/深拷贝对象,可以提高效率)
②引用做返回值(面对大对象/深拷贝对象,可以提高效率)
传值返回,会生成临时变量,发生两次拷贝;而传引用返回则不会生成临时变量!(*条件:使用引用返回时,返回值的生命周期不能属于该函数!!)
若引用返回的返回值是函数的局部变量:
若果函数结束,栈帧销毁,没有清理栈帧,那么ret的结果侥幸是正确的;
如果函数结束,栈帧销毁,清理栈帧,那么ret的结果是随机值。
注意:没有空引用,引用必须指向一个有效的对象,不能为空。这与指针不同,指针可以指向空。
常引用
例如:const int& m = 10; (10是常量,所以要m加const修饰,否则会导致权限放大问题)
引用的过程当中,权限不能放大,只能平移或缩小。对权限缩小的引用来说,权限缩小的是引用的别名,不会影响到原变量本身。
这样不行:
但这样就行了:
这是为啥??
--- 隐式类型转换会出现临时变量,即将dd的值拷贝给临时变量,然后将临时变量转化后的值再拷贝给ii,但是临时变量具有常属性,所以拷贝给别名时要加const修饰!!
c.引用和指针的区别
①引用必须在*定义时初始化,指针没有要求;
②引用在初始化一个实体后,就不能再引用其它实体,而指针可以在任何时候指向任何一个同类型实体;
③没有NULL引用,但有NULL指针;
④引用对指向的对象无需进行取地址和解引用操作,但指针xu
auto%E5%92%8C%E8%8C%83%E5%9B%B4for">2.auto和范围for
auto%E5%85%B3%E9%94%AE%E8%AF%8D">a.auto关键词
auto 关键字用于自动类型推导。当编译器能够确定变量的类型时,可以使用 auto 来让编译器自动推断变量的类型,而无需显式指定。
使用场景:
①迭代器和循环:
在使用容器(如 vector、list、map等)的迭代器时,迭代器的类型通常很长且复杂,所以,我们可以使用 auto 来简化代码:
②lambda 表达式:
在定义 lambda 表达式时,使用 auto 可以避免显式指定 lambda 的类型:
③类型推导:
在模板编程中,auto 可以用于推导模板参数的类型:
cout<<typeid(ref).name()<<endl; 可以打印出ref的类型 .
注意:
①auto 只能用于局部变量和函数参数(C++11 及以后),不能用于全局变量或类成员变量。
②使用 auto 时,变量的初始化是必须的,因为编译器需要通过初始化表达式来推导类型。
③当使用 auto 与引用或指针结合时,需要特别注意推导的结果。
例如,auto& x =
someExpression;
会推导 x 为 someExpression的引用类型。