目录
命名空间
在c++如果大量的变量、函数和类的名称都存放在全局作用域中的话,可能对导致很多命名冲突问题,比如自己定义的函数名和库里的函数名相同了。
c++引入了一个关键字namespace来解决这个问题
命名空间定义
namespace 命名空间名字{}
命名空间中可以定义变量/函数/类型
namespace n1
{
int a;
int b;
int fun(int x,int y)
{
return x+y;
}
}
命名空间也可以嵌套
namespace N1
{
int a;
namespace N2
{
int b;
}
}
一个命名空间定义了一个新的作用域,命名空间中所有的内容都局限域改命名空间中
命名空间的使用
- 加命名空间加作用域限定符 ::
printf("%d",N1::a);
- using将命名空间中的成员引入
using N1::a;
printf("%d",a);
- using namespace 命名空间 将命名空间名称引入
using namespace N1;
std命名空间
std是C++标准库的命名空间,C++将标准库的定义实现都放到这个命名空间中。
- 在日常练习中,建议直接using namespace std即可,这样就很方便。
- 在项目开发中最好使用第二种方法。指定命名空间 +using std::cout展开常用的库对象/类型等方式。
c++输入 输出
在c++中可以用cout输出cin输入
#include<iostream>
using namespace std;
int main()
{
cout<<"Hello world!!!"<<endl;
int a=0;
cin>>a;
return 0;
}
- 使用cout标准输出对象(控制台)和cin标准输入对象(键盘)时,必须包含< iostream >头文件
以及按命名空间使用方法使用std。 - cout和cin是全局的流对象,endl是特殊的C++符号,表示换行输出,他们都包含在包含<
iostream >头文件中。 - <<是流插入运算符,>>是流提取运算符。
- 使用C++输入输出更方便,不需要像printf/scanf输入输出时那样,需要手动控制格式。C++的输入输出可以自动识别变量类型
缺省参数
缺省参数是声明或定义函数时为函数的参数指定一个缺省值。在调用该函数时,如果没有指定实参则采用该形参的缺省值,否则使用指定的实参。
其实就是默认参数
在定义函数的时候给参数一个默认的值,如果函数调用时没有传参就使用参数的默认值。
缺省参数分为全缺省、半缺省
- 全缺省参数
void Func(int a = 10, int b = 20, int c = 30)
{
cout<<"a = "<<a<<endl;
cout<<"b = "<<b<<endl;
cout<<"c = "<<c<<endl;
}
- 半缺省参数
void Func(int a, int b = 10, int c = 20)
{
cout<<"a = "<<a<<endl;
cout<<"b = "<<b<<endl;
cout<<"c = "<<c<<endl;
}
- 半缺省参数必须从右往左依次来给出,不能间隔着给
- 缺省参数不能在函数声明和定义中同时出现
3.缺省的值必须时常量或者全局变量
函数重载
函数重载概念
函数重载是函数的一种特殊情况,C++允许在同一作用域中声明几个功能类似的同名函数,这些同名函数的形参列表(参数个数 或 类型 或 类型顺序)不同,常用来处理实现功能类似数据类型不同的问题。
函数的参数个数 类型 和顺序不同都可以构成函数重载
函数重载原理
要理解函数重载的原理首先要理解编译器编译的过程
编译器编译的过程:
- 预处理:完成头文件展开 宏替换 条件编译 去掉注释等功能 最后生成 .i文件
- 编译:语法分析 语法检查 符号汇总 生产汇编代码 .s文件
- 汇编:形成符号表 把汇编代码转化成二进制机器码 生产可重定位目标文件.o文件
- 链接:合并段表 符号表合并和符号表重定为
在编译链接的过程中,编译器在编译阶段会根据函数的名字将函数转化成符号,在汇编阶段形成符号表 表里面包含函数的地址。链接时再到符号表中找到这个函数的地址。
在C语言直接使用函数名来标识和查找函数,如果连个函数名相同在符号表中就会存在歧义,同时链接的时候也会冲突。所以C语言不支持重载。
而在c++中有函数名修饰规则,不是直接根据函数名来标识函数。 比如g++编译器会根据函数的长度 函数名 类型首字母来修饰函数名 这样就算函数名一样修饰后的名称也不一样。在符号表里重载的函数就不存在二义性和冲突了,链接查找地址时也很明确。
引用
应用就是给已经存在的变量取一个别名,引用不开空间 ,他和引用的变量使用同一块空间
类型& 引用变量名=引用实体
int a=10;
int& ra=a;
ra 就是a的引用,引用的类型必须和实体时同一类型的
引用的特性
- 引用在定义时必须初始化
- 一个变量可以有多个引用
- 引用引用了一个实体后就不能在引用其他实体
注意:
在引用时权限不能放大,但是权限可以不变或缩小。
比如一个变量是只读的引用也要是只读的就要使用const引用
const int a=10;
const int& ra=a;
引用的使用场景
- 引用做参数
void Swap(int& left, int& right)
{
int temp = left;
left = right;
right = temp;
}
引用时变量的别名共用同一快空间,引用做参数时当引用改变实参也会改变。不用再传地址。
如果不改变参数建议使用const引用
2.引用做返回值
int& Count()
{
static int n = 0;
n++;
// ...
return n;
}
传值返回时:会创建一个临时变量,返回临时变量
传引用返回:返回返回对象的引用
注意:
函数返回时出了函数的作用域,如果返回对象还在(还没有还给系统)就可以用引用返回,如果还给操作系统了就不能用引用返回
引用和指针的区别
1.引用是别名,不开空间
2.引用在定义时必须初始化
3.引用再初始化后就不能改变其指向
4.sizeof引用是实体大小 sizeof指针4或8
5.没有空引用
6.没有多级引用
7.引用自加是实体自加,指针自加是向后偏移一个类型大小
8.访问实体的方式不同
9.引用更安全,指针需要考虑空指针、野指针
内联函数
以inline修饰的函数
c++在编译时会在调用内联函数处将函数展开,不会建立函数栈帧,提升函数的运行效率。
1.inline是一种空间换时间的做法,在编译阶段会用函数体替换函数调用。可能会造成目标问价过大
2.inline对编译器而言只是一个建议。具体取决去编译器。不是递归且调用频繁可以使用inline
3.inline不要声明定义分开。因为符号表中不会生产inline函数的地址,会导致链接错误。
auto关键字
作用:根据右边的值自动推导类型
类型声明成auto,可以根据a的类型自动推导b的类型
int a = 10;
auto c = &a;
auto* d = &a;
//用这两个当成指针都可以,都是int*类型
int& y = a;
//类型为int,是a的引用
auto& z = a;
//类型为int,但z为a的引用
注意:
当在同一行声明多个变量时,这些变量必须是相同的类型,否则编译器将会报错,因为编译器实际只对第一个类型进行推导
auto不能推导的场景
1.不能用来声明数组
int a = {1,2,3};
auto b = {4,5,6};//不行
2.不能做参数
void TestAuto(auto a)//不行
基于范围的for循环
//c++11范围for
int arry[10]={1,2,3,4,5,6,7,8,9}
for (auto e:arry)
{
cout<<e<<endl;
}
自动依次取数组中的元素,赋值给e
范围for 后面必须是数组名
nullptr指针空值
NULL实际上是一个宏定义如下
#ifndef NULL
#ifdef __cplusplus
#define NULL 0
#else
#define NULL ((void *)0)
#endif
#endif
可以看出 NULL和常量0是等价的 所以再某些情况下可能会发生混淆
void f(int)
{
cout<<"f(int)"<<endl;
}
void f(int*)
{
cout<<"f(int)"<<endl;
}
int main()
{
f(0);
f(NULL);
f((int*)NULL);
return 0;
}
程序实际上想通过f(NULL)调用指针版本的(int*)函数,但是由于NULL被定义为0。所以会调用第一个函数。那么我们如果就想传一个空指针就要用(int*)NULL
所以c++y引入nullptr表示空指针
1. 在使用nullptr表示指针空值时,不需要包含头文件,因为nullptr是C++11作为新关键字引入
的。
2. 在C++11中,sizeof(nullptr) 与 sizeof((void*)0)所占的字节数相同。
3. 为了提高代码的健壮性,在后续表示指针空值时建议最好使用nullptr。