
文章目录
这里是think的博客
希望可以一起交流知识,一起think
今天我们来学习C++
一起来think
首先学习C++需要有C语言的基础知识,想学习C语言可以到我的其他博客看看。
1.namespace
cpp
#include<iostream>
using namespace std;
int main()
{
int a = 10;
cout << 10 << endl;
return 0;
}
第一个重要的点就是namespace,namespace是命名空间域,其中using的意思是展开,所以using namespace std;的意思是展开std这个命名空间域。
1.1为什么要有namespace

- 情况1:stdlib.h头文件中有rand函数, 此时rand既是全局变量,又是函数名,都在全局域中,命名冲突了,此时编译器不知道要使用那一个,进而报错。
- 情况2:当多人协作的时候,可能也会命名冲突,这个是最常见的,不像情况1那样是改一改变量名就行了,毕竟要协作的项目,可能一个变量名写了很多次,那么是你改还是他改呢?都没有义务改。
其中额外提一点,如果rand从全局变量(属于全局域)变成了局部变量(属于局部域),就不是命名冲突了,那么在main函数中使用rand,使用的是局部域里的,这个其实就是有局部有全局,使用的是局部的知识点了。
1.2怎么使用namespace
也正是有这些实际的问题,C++中就新增了一个namespace关键词,即命名空间域。
cpp
#include<stdio.h>
#include<stdlib.h>
namespace cpp
{
int rand = 10;
}
int main()
{
printf("%p", rand);
return 0;
}
这里用到的就是rand函数而不是rand变量了,因为我们加上了cpp这个命名空间域,编译器默认是不会去命名空间中找的。
那么怎么使用cpp中的rand变量呢?
使用域作用限定符::
cpp
#include<stdio.h>
#include<stdlib.h>
namespace cpp
{
int rand = 10;
}
int main()
{
printf("%d", cpp::rand);
return 0;
}
C语言中还有一个问题也可以用域作用限定符解决,一个全局变量和一个局部变量,默认使用局部,前面只加上::就可以使用全局的了。
cpp
#include<stdio.h>
int rand = 100;
int main()
{
int rand = 10;
printf("%d", rand);
printf("%d", ::rand);
return 0;
}
- namespace中不仅可以定义变量还可以定义类型和函数。
- namespace可以嵌套定义
cpp
#include<stdio.h>
namespace cpp
{
namespace c
{
int rand = 100;
}
}
int main()
{
printf("%d", cpp::c::rand);
return 0;
}
1.2.1using
using namespace cpp是展开cpp命名空间,和头文件的展开对比来看,头文件的展开是将头文件中的内容拷贝到原处,而命名空间的展开表示的是让编译器查找名称出处的时候会去该展开的命名空间中查找。
问题:如果展开了两个命名空间,但是内部有名称相同的变量,就会命名冲突,所以using一般使用在我们日常写代码的时候,在项目中也可以使用,但是一般是这样使用
using cpp::rand表示展开cpp中的rand,即使用rand时查找出处的时候会去cpp中找,而大的项目中一般不会直接把命名空间的全部内容都展开,一般是靠这个展开,常用又不容易冲突的。
1.3namespace在使用中的注意事项
- 我们写程序的时候经常会头源文件分离,会在头源文件中写两遍命名空间,在头文件展开的时候,命名空间不会冲突,而是会合并,当然不是物理上的拼在一起,而是逻辑上的合并。
- C++中有4个域,函数局部域,全局域,命名空间域和类域,其中局部域和全局域生命周期就是局部和全局,但命名空间域和类域只会改变作用域不会改变生命周期,其中命名空间不可以定义在局部,所以可以说命名空间的生命周期是全局的,但是类域可以定义在全局也可以定义在局部。
2.C++的输入输出
区别于C语言的printf和scanf函数,C++中输入输出靠的是istream和ostream类的对象,cin和cout
2.1为什么要有cin和cout
原本有了printf和scanf了,已经可以进行输入输出了,那么为什么C++中还要引入cin和cout呢?
2.1.1从面向过程到面向对象
- cin和cout实际上是为了实现C++面向对象的特性而诞生的,因为我们在使用printf和scanf函数的时候其实是需要我们告诉函数我要用什么格式打印数据,我要用什么格式读取数据,这里是面向过程,但是我们要的是面向对象,所以我们设计出cin和cout,而cin和cout是自己识别格式。
- 其中cin和cout还可以支持自定义类型的输入输出
现在的知识只能提到这两个点,以后的点以后再讲。
2.2cin和cout的使用
cin和cout在std域内,也在iostream头文件内。
cpp
#include<iostream>
#include<stdio.h>
using namespace std;
int main()
{
int a = 10;
float b = 10.3;
int c = 0;
cin >> c;
cout << a << endl << b << endl << c << endl;
return 0;
}
其中endl是一个函数,现在可以认为endl的作用是换行加刷新缓冲区。(C++中的换行不刷新缓冲区)
<<和>>是流插入和流提取操作符,其中它们也是左移,右移操作符。
iostream可能会间接包了stdio.h,当然不是所有编译器都会只有,这是编译器的未规定行为。
2.2.1竞赛中节省时间的代码
cpp
#include<iostream>
using namespace std;
int main()
{
// 在io需求⽐较⾼的地⽅,如部分⼤量输⼊的竞赛题中,加上以下3⾏代码
// 可以提⾼C++IO效率
ios_base::sync_with_stdio(false);
cin.tie(nullptr);
cout.tie(nullptr);
return 0;
}
- 第一句表示解除 C++ 标准输入输出流(cin/cout)与 C 标准输入输出流(scanf/printf)之间的同步,使 C++ IO 使用独立的缓冲区,从而提高输入输出效率。
- 第二句表示解除 cin 和 cout 之间的绑定关系。默认情况下,cin 在进行输入前会自动刷新 cout 的输出缓冲区;解除绑定后,cin 不再自动刷新 cout,可以减少额外开销,提高效率。
敢这样也是因为竞赛中出现大都是大量输入之后(比如求和一百万次)再输出一次最后的结果,所以解除绑定以后,可以减少cin输入前都要检查cout的输出缓冲区的时间
- 解除 cout 与其他输出流之间的绑定关系。(一般没用,因为cout默认没有绑定其他输出流)
没有解除。

有解除。

3.缺省参数
缺省参数其实就是让参数有默认值,如果传值的话就用传递的值,没有的话就用缺省的。
cpp
#include<iostream>
#include<stdio.h>
using namespace std;
int Add(int a, int b = 1)
{
return a + b;
}
int main()
{
int c = Add(1, 3);
cout << c << endl;
c = Add(1);
cout << c << endl;
return 0;
}
3.1为什么要有缺省参数?
- 为了减少多余的参数,因为可能一个参数写了很多遍都还是一个固定的值,那么缺省的作用就有了,直接将这个参数变为缺省参数即可,减少多余代码。
- 一般语言在更新的时候都会兼容前面的版本,就是因为可能很多项目用了之前版本的语法,这样更新可以保证旧代码在新编译器下也可以运行,这里也是一样,如果我们要改变一个函数的参数,从3个变为4个,但是已经有很多的接口都是3个的,这里采用缺省参数就可以避免报错。
- 日期类等类一般也需要缺省参数来保证不传参的时候也会默认值。
3.2缺省参数的使用时的注意点
- 缺省参数只能从右到左填,并且不可以跳跃填,而调用的时候实参只能从左到右填
这个语法也是可以改的,但是要引入占位符,但是C++连C语言中的printf和scanf占位符都没有引入到cout和cin上,也可以见得缺省参数中也没有占位符,所以在没有占位符的情况下如果不遵守这个规则就会产生歧义。
错误代码
cpp
#include<iostream>
#include<stdio.h>
using namespace std;
int Add(int a = 1, int b)
{
return a + b;
}
int main()
{
int c = Add(1, 3);
cout << c << endl;
c = Add(1);
cout << c << endl;
return 0;
}
在没有占位符情况下,编译器无法判断这个1是传给a的,还是b的,C++官方不支持这种语法c = Add(,1);(要是有占位符这种写法是可以实现的)
- 值得注意的是可能有人认为在处理缺省参数的时候是在运行中处理的,认为是在执行到调用函数那里去到函数内部发现有缺省参数,于是就给对应的参数赋缺省值。
实际上是在编译期间处理完了,比如
Add(1);在编译阶段直接当成:Add(1,1);来处理。
- 缺省参数分为全缺省(全部参数都缺省)和半缺省参数(一部分参数缺省,要从右到左,不能跳跃)。
- 函数声明和定义分离时,不能同时在声明和定义给缺省值,规定必须在函数声明中给。
因为如果函数声明和定义分离,那么编译器是根据声明来处理缺省参数,因为是分离编译,所以要在声明中给缺省参数,而在定义中给缺省参数是未定义行为,可能可以通过,比如VS2022可以通过,以声明为主,定义中的直接忽略,也可能会报错,如果定义和声明的缺省参数一致,那么就是重复定义,不一致就是定义冲突。
4.函数重载
在同一作用域内函数名可以相同。
cpp
#include<iostream>
#include<stdio.h>
using namespace std;
int Add(int a, int b)
{
return a + b;
}
float Add(float a, float b)
{
return a + b;
}
int main()
{
int c = Add(1, 3);
cout << c << endl;
float d = Add(1.1f, 2.2f);
cout << d << endl;
return 0;
}
4.1为什么要有函数重载
- C语言中如果我们要写int和float类型的加函数的话,函数名应该为Addi和Addf,调用的时候也是比较麻烦,现在就可以直接定为Add了,调用的时候写的都是Add。
- 函数重载就是为了帮助记忆和让代码更加的清晰。
- 函数重载也初步体现了把事情交给编译器做的特性。
4.2函数重载注意点
4.2.1返回值不同不构成函数重载
函数重载成立的条件是同一作用域内,同名函数,参数个数或者类型不同。
其中函数返回值不同不算,因为如果参数个数和类型相同,但是返回值不同,也是重载的话,会产生歧义,因为在函数调用的时候使用的是函数名和函数参数,没有用到返回值的,所以编译器将会不知道调用哪一个函数。
错误代码
cpp
#include<iostream>
#include<stdio.h>
using namespace std;
int Add(int a, int b)
{
return a + b;
}
float Add(int a, int b)
{
return a + b;
}
int main()
{
int c = Add(1, 3);
cout << c << endl;
return 0;
}
这里就有歧义了,编译器不知道调用哪一个。
4.2.2构成函数重载产生歧义的情况
有着全缺省参数的函数和无参的函数是构成函数重载的,满足函数参数的个数或者类型不同的条件,至于是否有缺省参数和是否构成函数重载无关。
cpp
#include<iostream>
#include<stdio.h>
using namespace std;
int fun()
{
return 1;
}
int fun(int a = 10)
{
return a;
}
int main()
{
int c = fun();
cout << c << endl;
return 0;
}
这里有歧义,编译器不知道调用哪一个函数,如果换成fun(1);就可以了,就调用的是第二个fun。
那么这种情况下如果还想要保持函数重载的话,这里的歧义没有办法解决,不要这样调用即可,但是如果不要保持函数重载了,可以给其中一个加上namespace命名空间即可。
5.总结
谢谢观看!