目录
一、命名空间namespace的作用与使用方法
二、C++的输入与输出
三、缺省参数的规则与介绍
四、函数重载(为什么C语言不支持函数重载而C++支持重载呢?C++是如何支持重载的?
extren "c"的作用是啥?)
五、引用
六、内联函数(与宏函数对比)
七、auto的使用
八、范围for
九、C++空指针nulllptr
在C++中,变量、函数,和类都是大量存在的,这些变量的名称都存在于全局作用域中,可能会导致很多冲突,使用命名空间的目的是对标识符的名称进行本地化,以避免命名冲突或名字污染就,使用namespace可以较好的解决这个问题。
举几个例子:
#include
int main()
{
int scanf=10;//定义一个名字为scanf的变量 与输入函数冲突
int strlen=20;
scanf("%d",&scanf);
scanf("%d",&strlen);
printf("%d,%d",strlen,scanf);
return 0;
}
这个代码这样写是无法完成编译的 因为我们定义了一个局部变量scanf 库里也有一个scanf函数 但是编译器会按照就近原则去找scanf 找到的就是变量scanf而不是scanf函数 所以会报错 这就是标识符冲突了 重名了,那么怎么解决呢?
#define _CRT_SECURE_NO_WARNINGS
#include
namespace test
{
int scanf = 10;
int strlen = 20;
}
int main()
{
//int scanf = 10;//定义一个名字为scanf的变量 与输入函数冲突
//int strlen = 20;
/* scanf("%d", &scanf);
scanf("%d", &strlen);*/
printf("%d,%d",test::strlen, test::scanf);
return 0;
}
我们命名一块空间 在这块空间里面定义scanf和strlen 绕后通过::域作用限定符去访问指定的test域(命名空间)里的scanf和strlen就不会发生命名冲突了。
还有就是namespace可以嵌套使用
在A中 嵌套了B 然后通过域作用限定符去访问和嵌套访问
注:当::前面没有标明指定的域 那么编译器就会默认是全局域 (如 ::a,如果定义了一个局部变量a和一个全局变量a 那么就会去访问全局范围的a而不是局部变量a 因为::前面为空 默认访问的是全局范围里的变量,不会优先局部变量)
小知识:
1、一个命名空间就定义了一个新的作用域,命名空间中所有内容都局限于该命名空间中
2、同一个工程中允许存在多个相同名称的命名空间,编译器最后会把它们合成成同一个命名空间
C++库为了防止命名冲突,把自己库里的东西定义在了一个std的命名空间中,要使用标准库里的东西有三种方式:
1、指定命名空间(比较麻烦,但是最规范)
2、把std整个展开,相当于库里的东西全到全局域了(虽然方便但是规范的工程中会杜绝这种方式,平时刷题可以用,因为一旦std库里的东西与我们自己定义的东西冲突了就解决不了了)
3、对部分常用的库里面的东西进行展开(1和2方法的折中解决方法)
注:部分展开只能一行一行地写std::
using namespace std;//第二种方法
using std::cout;//第三种方法
using std::endl;//第三种方法
int main()
{
int a = 0;
std::cout << a << std::endl;//第一种方法
}
C++里的输入输出和输出都是对象,其中cin是输入,是istream这个类的全局对象,cout是输出,是ostream这个类的全局对象,endl是全局的换行符号。
使用cout标准输出(控制台)和cin标准输入(键盘)时,必须包含< iostream >头文件以及std标准命名空 间。 注意:早期标准库将所有功能在全局域中实现,声明在.h后缀的头文件中,使用时只需包含对应头文件 即可,后来将其实现在std命名空间下,为了和C头文件区分,也为了正确使用命名空间,规定C++头文 件不带.h;旧编译器(vc 6.0)中还支持格式,后续编译器已不支持,因此推荐使用 +std的方式。
C++中的输入和输出会自动识别变量的类型 不需要像C语言那样控制格式
using namespace std;
int main()
{
int a = 1;
int b = 2;
cout << a << endl;
cout << b << endl;
cout << "hello word!" << endl;
return 0;
}
缺省参数就类似与现实生活中的备胎!
缺省参数是声明或定义函数时为函数的参数指定一个默认值。在调用该函数时,如果没有指定实参则采用该 默认值,否则使用指定的实参。
规则:缺省包括全缺省和半缺省,其中半缺省规定必须是从右往左依次缺省的,并且是连续的
以Add函数为例:
假如调用Add函数的时候没有传参数 会默认使形参为给的缺省值
注:如果声明与定义位置同时出现 并且两个位置提供的值不同 那么编译器会不知道该用哪个缺省值(缺省值只能在声明与定义其中的一个里出现,否则会报错) 还有缺省值必须是常量或者全局变量
函数重载:是函数的一种特殊情况,C++允许在同一作用域中声明几个功能类似的同名函数,这些同名函数的 形参列表(参数个数 或 类型 或 顺序)必须不同,常用来处理实现功能类似数据类型不同的问题
以Add函数为例
加法包括整形加法 字符加法 浮点数加法
那么通过重载就可以实现多种类型的加法了
重载也会冲突,使用要注意!!
例如:
调用f两个构成重载的函数f 都符合 会编译器摸不着头脑!!
两个问题:
1、C语言为什么不支持函数重载?C++为什么支持函数重载 ?是怎么支持的?
2、extern "C"的作用是什么?
1、
c++中会有一个函数名修饰规则 每个函数在汇编中都有一个call 函数名(函数地址)
经过函数名修饰规则修饰后的函数名个不相同尽管构成重载的函数修饰后的名字也是不同的 所以编译器可以区分出两个函数 但是C语言中没有函数名修饰规则 那当两个函数同名时 编译器分不清我们要调用那个 这就是c++支持函数重载但是C语言不支持的原因
2、extern “c”的作用是让用c++写的函数可以在c环境和c++ 环境下都可以正常调用
有时候在C++工程中可能需要将某些函数按照C的风格来编译,在函数前加extern "C",意思是告诉编译器, 将该函数按照C语言规则来编译。比如:tcmalloc是google用C++实现的一个项目,他提供tcmallc()和tcfree 两个接口来使用,但如果是C项目就没办法使用,那么他就使用extern “C”来解决。
引用不是新定义一个变量,而是给已存在变量取了一个别名,编译器不会为引用变量开辟内存空间,它和它 引用的变量共用同一块内存空间。(可以理解成指针)
类型& 引用变量名(对象名) = 引用实体;
例如交换函数
可以操作地址将a b交换
亦可以通过引用(取别名)让形参就是实参本身 对实参的操作就是对形参的操作 (引用本质是地址,只是语法上说成引用,简化了指针的概念 更加简便)
引用特点:
1. 引用在定义时必须初始化
2. 一个变量可以有多个引用
3.引用只能引用一个实体,只能在初始化的时候引用一次 ,不能更改为转而引用其他变量。
常引用
int main()
{
const int a = 10;
int& b = a;
int c = 10;
const int& d = c;
return 0;
}
变量加了const修饰就具有了常属性 不能改变 那么被const修饰的变量的引用就不能随意的改变其所引用的对象的值 否则就是权限放大了 其引用必须也要加上const修饰才可以,上图a的引用就不合法 c的引用合法(权限缩小了 可以 但是不能使权限放大 )
引用的作用
1、做参数(减去函数传参时为拷贝参数而创建临时变量)
定义一个结构体 结构体成员是一个长度为10000的整形数组 Func1函数的参数是结构体但Func2的参数是结构体的引用 可以看到调用两个函数10000 次 的时间差是很大的 一个是31ms 一个是0ms ,因为Func1是传值传参,函数是不会直接传递实参给形参,而是传递实参的一份临时拷贝(会有时间消耗),而用引用做参数就不会有实参的临时拷贝,而是形参就相当于是实参 直接拿来用就是了,所以说用引用当做函数的参数可以避免传参时的参数拷贝 可以减少时间,提高程序运行效率。
2、做返回值
函数在返回一个变量的值的时候不会直接将变量本身返回,而是会返回需要返回的变量的临时拷贝,所以如果是返回的值就会有拷贝的过程 如果返回的数据量比较大的时候拷贝消耗的时间就多了,但是如果是返回的引用的话就没有拷贝这个过程了,就会节省时间,提高效率。(但是返回值是引用的方法必须是要返回的值在出了当前函数的作用域内仍然存在没有被销毁才可以,否则返回的就是被销毁的栈帧的数据,如果这块栈帧没有别其他函数占用,里面的数据一般不会变,但是很大的可能里面的数据是会改变的 ,所以返回引用要保证引用的那个对象不会在出了函数作用域后被销毁)
以inline修饰的函数叫做内联函数,编译时C++编译器会在调用内联函数的地方展开,没有函数压栈的开销, 内联函数提升程序运行的效率。
例如Add函数 是一个小型函数 如果调用的次数多了 那么就要每次后开辟栈帧 浪费所以就写成内联函数 在调用的地方展开就可以。
但是代码很长的或者有循环和递归的函数不适合用作内联函数 效率低,这也是为什么不是所有的函数都可以写成内联函数的原因
注意:内联函数不建议声明和定义分离,分离会导致链接错误,因为inline 被展开就没有函数地址,链接就找不到
inline Add(int a,int b)
{
int c=a+b;
return c;
}
c语言为了函数避免建立栈帧,提供了宏函数支持,预处理阶段展开,既然C语言有了解决方法那么c++为什么还要引入内联函数呢?(宏函数的缺点)
1、宏函数不支持调试
2.、宏函数语法复杂,容易出错
3、没有类型安全检查
内联函数弥补了宏函数的缺陷!
使用auto定义变量时必须对其进行初始化,在编译阶段编译器需要根据初始化表达式来推导auto的实际类 型。因此auto并非是一种“类型”的声明,而是一个类型声明时的“占位符”,编译器在编译期会将auto替换为 变量实际的类型。
#include
int main()
{
int a=0;
auto c=a;//使用auto关键字会自动把c的类型设为int
return 0;
}
注意:
1. auto与指针和引用结合起来使用 用auto声明指针类型时,用auto和auto*没有任何区别,但用auto声明引用类型时则必须加&
2. 在同一行定义多个变量 当在同一行声明多个变量时,这些变量必须是相同的类型,否则编译器将会报错,因为编译器实际只对 第一个类型进行推导,然后用推导出来的类型定义其他变量。
3. auto不能作为函数的参数
4. auto不能直接用来声明数组(auto int[8]是错的)
#include
int main()
{
int a[10]=0;
for(int i=0;i<10;i++)
{
a[i]=i;
}//普通循环
for(int x:a)
{
cout<
但是注意要通过范围for来改变数组里的数,那么就要用引用,因为(int x: a)中的x是一个局部变量
如果我们改变x是不能改变数组中的数的 所以要把int 改成int& 就是引用了 引用就是拿到了数组中的数 改变引用就改变了数组的数据
在C语言中的空指针是NULL 这是一个宏 值是0 这样的定义方法有时候是很不好的 所以在c++中用nullptr 来代替NULL