C++ 基础知识

发布于:2024-10-12 ⋅ 阅读:(9) ⋅ 点赞:(0)

目录

一.命名空间和输入输出流

1.命名空间

a.命名空间的由来

b.命名空间的使用

①命名空间的定义

②命名空间的展开

③域作用访问限定符

2.输入输出流

a.基本概念

b.标准输入输出流

二.缺省参数和函数重载

1.缺省参数

a.全缺省

b.半缺省

2.函数重载

函数名修饰规则

三.引用和范围for

1.引用

a.引用的定义和初始化

b.引用的使用场景

c.引用和指针的区别

2.auto和范围for

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

2.auto和范围for

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的引用类型。

b.范围for