✨前言✨
🎓作者:【 教主 】
☕博主水平有限,如有错误,恳请斧正。
📌机会总是留给有准备的人,越努力,越幸运!
💦导航助手💦
目录
命名空间
什么是命名空间?
命名空间就是一个域,在这个区域我们可以创建变量,创建函数,创建类型,还可以嵌套命名空间。命名空间内的所有内容都属于该命名空间。
如何创建命名空间?
创建命名空间采用 namespace+命名空间名 的形式。
举例如下:
创建了名为xxx的命名空间,该命名空间内有变量,结构体类型,以及函数。
为什么有命名空间?
命名空间可以将一段代码有效的进行封装,用来处理相同的变量名等问题。
不同文件下的相同名的命名空间会合并,例如在同一个工程下有两个cpp文件,这两个cpp文件中都有命名空间xxx,那么最终编译器会将两个xxx合并为一个。
如何使用命名空间?
-
将命名空间全部展开(不推荐)
举例如下:
#include<iostream>
using namespace std;
int main()
{
cout << "hello world" << endl;
return 0;
}
使用格式:using namespace + 命名空间名 即可将命名空间全部展开,直接使用命名空间内的内容,上述代码将命名空间std展开,使用std命名空间中的cout。
缺点:如果两个命名空间都有同一名称的变量等,命名空间全部展开可能会造成重定义
-
展开常用的(推荐)
举例如下:
#include<iostream>
using std::cout;
using std::endl;
int main()
{
cout << "hello world" << endl;
return 0;
}
使用格式:using 命名空间名::要使用的内容 即可展开该命名空间内常用的内容。
-
不展开(麻烦)
举例如下:
#include<iostream>
int main()
{
std::cout << "hello world" << std:: endl;
return 0;
}
使用格式:命名空间名::要使用的内容即可。
C++输入&输出
#include<iostream>
int main()
{
std::cout << "hello world" << std:: endl;
return 0;
}
std是C++标准库的命名空间名,其中包含了cout和cin,cout是标准输出对象(控制台),cin是标准输入对象(键盘),endl是C++符号,表示换行输出,它们都包含在头文件<iostream>中。
<<是流插入运算符,>>是流提取运算符。
注意:这里只是简单介绍,其中涉及到运算符重载等知识,后续会继续介绍。
C++会自动识别变量类型
#include<iostream>
using namespace std;
int main()
{
char a;
int b;
float c;
cin >> a >> b >> c;
cout << a << b << c;
return 0;
}
C++不用像C语言一样设置输入输出的格式等问题,会自动识别。
缺省参数
什么是缺省参数?
缺省参数是在声明和定义函数时为函数的参数指定一个缺省值,调用该函数时,如果没有指定实参则采用该形参的缺省值,否则使用指定的实参。
#include<iostream>
using namespace std;
void print(int num = 0)
{
cout << num << endl;
}
int main()
{
print();
//未传递实参,采用缺省值
print(10);
//传递实参,采用实参
return 0;
}
运行结果如下:
缺省参数分类
- 全缺省参数
void print(int num1 = 1, int num2 = 2)
{
cout << num1 << num2 << endl;
}
- 半缺省参数
void print(int num1, int num2 = 2)
{
cout << num1 << num2 << endl;
}
注意 :
- 半缺省参数必须从右到左依次给出,不能间隔给
- 缺省参数不能在定义和声明中同时出现
函数重载
C++允许在同一个作用域中声明几个功能类似的同名函数,这些同名的形参列表(参数个数或类型或类型顺序不同)。
#include<iostream>
using namespace std;
//参数类型不同
int add(int num1, int num2)
{
return num1 + num2;
}
double add(double num1, double num2)
{
return num1 + num2;
}
//参数个数不同
void fun()
{
;
}
void fun(int num)
{
;
}
//参数顺序不同
void fun(int num, char ch)
{
;
}
void fun(char ch, int num)
{
;
}
int main()
{
add(1, 2);
add(1.1, 2.2);
fun();
fun(1);
fun(1, 'a');
fun('a', 1);
return 0;
}
注意:返回类型不能作为函数重载的依据。 因为调用函数时可以不接收返回值,这时编译器也不清楚究竟在调用哪个函数。
引用
什么是引用?
引用是一个变量的别名,例如梁山好汉里面的宋江,江湖上人称及时雨,及时雨指的就是宋江。
引用不是新定义一个变量,而是给已经存在的变量取了一个别名,编译器不会给引用开辟内存空间,它和它引用的变量共用一块内存空间。对引用的操作相当于对变量操作。
void test()
{
int num = 10;
int& rnum = num;
rnum = 20;
cout << num << endl;
}
运行结果如下:
引用特性
- 引用在定义时必须初始化
- 一个变量可以有多个引用
- 引用一旦引用一个变量,不可引用其他变量
常引用
void test()
{
const int a = 10;
//int& ra = a; 编译出错,a为常量
const int& ra = 10;
//int& b = 10; 编译出错,10为常量
const int& b = 10;
double d = 1.2;
//int& rd = d; 编译出错,类型不同
}
使用引用时需要注意的问题
- 引用权限不能放大
void test()
{
const int num = 10;
int& rnum = num;//权限放大
}
num具有const属性,不能更改,但是引用rnum可读可写的,引用不能放大权限,所以引用也应该声明为const属性。
- 使用类型转换的引用时加const
void test()
{
int num = 10;
float& rnum = num;//报错
}
上述代码会直接报错,原因是num是整形,而float是浮点型,实际上这里num是赋值给一个中间常量temp,再让rnum来引用这个temp,对于常量引用所以要加const。可能有很多人不理解为什么会有一个中间常量?实际上在强制类型转换时也有这样一个中间常量,例如要将int类型转换为float类型,原本的int类型并没有转换为float类型,而是存在一个中间变量来完成。
- 函数返回值返回局部对象时一定不能用传引用返回,要传值返回
出了函数作用域,局部对象已经销毁,这时传引用可能会引用未知问题
说明
- 引用作为参数传参时相比指针更加易于理解
- 引用在传递大对象时效率很高
- 做返回值时可以修改返回对象
指针与引用区别
- 引用定义一个变量的别名,指针存储一个变量地址
- 引用在定义时必须初始化,指针无要求
- 引用在初始化引用一个实体后,不能再引用其他实体,指针任何时候都可以指向任何一个同类型的实体
- 没有空引用,但是有空指针
- sizeof中的含义不同,引用结果是引用类型的大小,而指针是地址空间所占字节数
- 引用自加是引用实体加一,指针是向后便宜一个类型的大小
- 指针需要解引用,引用不用
- 引用更加安全
探索引用底层
void test()
{
int num1 = 10;
int& rnum = num1;
int num2 = 20;
int* pnum = &num2;
}
将上述代码转为汇编代码后,有如下结果
可能对汇编语言不太了解,但是我们可以看到,指针和引用在汇编层面是一样的。
内联函数
用关键字inline修饰的函数叫做内联函数,编译时编译器会将内联函数在调用的地方展开,没有函数调用建立栈帧的开销,将提升程序运行的效率。
特性
- 内联函数是一种以空间换时间的方法,减少函数调用建立栈帧的开销
- 内联函数只是一种建议,函数规模较小,不是递归,频繁调用的函数使用inline修饰一般会展开,如果较复杂则编译器会忽视inline
- 内联函数不建议声明和定义分离,分离会导致链接错误,因为inline被展开,没有函数地址,链接找不到
使用内联函数应注意的问题
- 内联函数只是对编译器的一种建议,具体会不会当作内联函数来使用,取决于编译器。
- 内联函数内部不能有递归、循环,这样如果展开反而会降低效率
内联函数与宏
相同点
- 提高程序运行效率
不同点
- 宏不可以调试,内联函数可以调试
- 宏没有参数类型检查,内联函数检查参数类型
auto关键字
随着程序越来越复杂,程序中用到的类型也越来越复杂,例如类型:std::map<std::string,std::string>::iterator,类型名过长过复杂,容易写错。
auto是一个新的类型提示符来指示编译器,auto声明的变量必须由编译器在编译时期推导而得。
#include<iostream>
using namespace std;
int fun()
{
return 10;
}
void test()
{
int a = 10;
auto b = a;
auto c = 'a';
auto d = fun();
//auto e; 编译出错,使用auto定义变量时必须进行初始化
}
int main()
{
test();
//测试函数
return 0;
}
auto使用规则
- auto与指针和引用结合使用
用auto声明指针类型时,auto* 和auto没有区别,但是用auto声明引用类型时必须加&
- 同一行定义多个变量
当在同一行声明多个变量时,这些变量必须时相同的类型,否则编译器会报错,因为编译器实际上只对第一个类型进行推导,然后用推导出来的类型定义其他变量
void test()
{
auto a=1,b=2;
auto c=3,d=4.0; //这行代码会编译失败,c和d的初始化类型不同
}
auto不能推导的场景
- auto不能作为函数参数
- auto不能用来声明数组
基于范围的for循环
for循环后的括号由冒号分为两部分,第一部分表示范围内用于迭代的变量,第二部分表示被迭代的范围。
void test()
{
int arr[] = { 0,1,2,3,4 };
for (auto& e : arr)
{
e *= 2;
}
for (auto e : arr)
{
cout << e << " ";
}
}
运行结果如下:
指针空值nullptr
原来我们使用的是空指针标识符是NULL,实际上NULL是一个值为0的宏,使用时可能出现以下问题
void fun(int)
{
cout << "fun(int)" << endl;
}
void fun(int*)
{
cout << "fun(int*)" << endl;
}
void test()
{
fun(0);
fun(NULL);
fun((int*)NULL);
}
运行上述代码结果为:
我们想让第二个fun()函数调用参数为int*的函数,却调用了参数为int的函数,出现了问题。因此引入nullptr,这里可以理解成nullptr是(void*)0。
欢迎关注,码字不易,希望多多点赞、收
藏哦!抱拳了。