欢迎来到Cefler的博客😁
🕌博客主页:那个传说中的man的主页
🏠个人专栏:题目解析
🌎推荐文章:题目大解析2
目录
👉🏻命名空间
命名空间概念
命名空间可以被看作是一个包含了一组相关实体的容器。通过将实体放置在命名空间中,可以将它们与其他代码隔离开来,避免命名冲突。
其语法就是:
namespace 空间名
{ }
在空间里面,我们也还可以创建这些👇🏻
- 变量
- 数组
- 结构体
- 函数
- 嵌套空间
引用空间⭐️
以上是我们自己创建空间,而我们平常还会引用已经写好的空间。
而这里又分为两种形式
- 全部展开
示例如下👇🏻
namespace class1
{
int a = 1;
int b = 2;
}
using namespace class1;
int main()
{
cout << a << endl;
cout << b << endl;
return 0;
}
上述代码里我创建了一个命名空间,并且对其全部展开,而后能顺利打印空间中的变量a,b。
2.部分展开
示例如下👇🏻
namespace class1
{
int a = 1;
int b = 2;
}
using class1::a;
这里我们用作用域限定符: :去展开空间中的部分变量a,此时b就无法被访问到了,而且部分展开时,是不用在using 后面加上namespace的。
那如果我只部分展开了部分变量,我还想访问其中的其它变量怎么办?
我们仍然可以用作用域限定符: :去针对性的访问空间中的某个变量,比如我想打印b👇🏻
cout << class1::b << endl;
🤔命名空间和头文件有什么区别?
命名空间和头文件是C++中的两个不同的概念。
命名空间是一种用于组织代码的机制,它可以将相关的函数、类、变量等放置在一个逻辑上的容器中,以避免命名冲突。通过使用命名空间,我们可以将代码模块化,并且可以在不同的命名空间中定义相同名称的实体而不会发生冲突。
头文件是一种用于包含代码的文件,通常包含函数、类、变量的声明和定义。头文件通常用于在多个源文件中共享代码,以避免重复编写相同的代码。通过包含头文件,我们可以在源文件中使用头文件中定义的实体,而无需重新编写它们的声明和定义。
因此,命名空间用于组织代码,避免命名冲突,而头文件用于共享代码和声明实体。
👉🏻 输入输出
- 使用cout标准输出对象(控制台)和cin标准输入对象(键盘)时,必须包含< iostream >头文件
以及按命名空间使用方法使用std。 - cout和cin是全局的流对象,endl是特殊的C++符号,表示换行输出,他们都包含在包含< iostream >头文件中。
- <<是流插入运算符,>>是流提取运算符。
- 使用C++输入输出更方便,不需要像printf/scanf输入输出时那样,需要手动控制格式。C++的输入输出可以自动识别变量类型
👉🏻 缺省参数
缺省参数概念
缺省参数是声明或定义函数时为函数的参数指定一个缺省值。在调用该函数时,如果没有指定实参则采用该形参的缺省值,否则使用指定的实参。
给的实参数目要和缺省数目一样
- 全缺省参数
即函数参数中都有给缺省值
void Func(int a = 10, int b = 20, int c = 30)
{
}
- 部分缺省参数
void Func(int a , int b = 20, int c = 30)
{
}
部分缺省参数需要注意
- 半缺省参数必须从右往左依次来给出,也不能间隔着给
错误案例👇🏻
void Func(int a = 10, int b , int c = 30)
{
}
- 缺省参数不能在函数声明和定义中同时出现,且缺省参数只能在声明中给
为什么呢?首先因为声明是我们能看的见的,我们在声明中给了缺省值,但定义中不给,就是怕定义中的缺省值和声明不一样,这就很矛盾了,所以为了避免这种矛盾问题,缺省值就只在声明中给。 - 缺省值必须是常量或者全局变量
- C语言不支持(编译器不支持)
👉🏻函数重载
概念
是函数的一种特殊情况,C++允许在同一作用域中声明几个功能类似的同名函数,这些同名函数的形参列表(参数个数 或 类型 或 类型顺序)不同
函数重载的好处是可以提高代码的可读性和灵活性。通过使用相同的函数名,我们可以根据不同的参数类型来执行不同的操作,而不需要为每种操作定义一个新的函数名。
重载的定义是方法名相同、参数类型不同,满足此条件即可,对返回值没有规定。
参数类型相同则不能称为重载,所以参数类型相同、但返回值不同的重载是不合法的。
但是函数重载可能会出现调用歧义的问题。如下👇🏻
#include<iostream>
using namespace std;
void func(int a)
{
cout << a << endl;
}
void func(int a,int b = 10)
{
cout << a + b << endl;
}
int main()
{
func(4);
return 0;
}
为什么c++能支持函数重载
我们学过,一个程序的运行要进行四个步骤:预处理,编译,汇编,链接
汇编过程就是将汇编代码转换为二进制,并且形成符号表,为的就是后面函数调用时可以根据函数名找到函数。
但是c和c++有不同的函数名修饰规则
- c语言:编译之后,函数名字的修饰不会发生改变
- c++: 编译之后,函数名字的修饰会发生改变,编译器会根据函数参数类型,和函数名大小对函数名进行修饰。
通过这里就理解了C语言没办法支持重载,因为同名函数没办法区分。而C++是通过函数修
饰规则来区分,只要参数不同,修饰出来的名字就不一样,就支持了重载
👉🏻引用
🧋引用概念
引用的实质并不是创建一个新的变量,引用是给一个已经存在的变量取的一个别名,这个别名可以代表这个变量。
📢引用使用注意要点:
- 引用必须初始化
- 引用的过程可以平移、缩小,但不可以放大
int main()
{
const int a = 0;
const int& a1 = a;//权限的平移
int b = 0;
const int& b1 = b;//权限的缩小
const int c = 0;
int& c1 = c;//权限的放大
return 0;
}
🤔什么情况是放大权限?
如果原变量不允许被改变,则任何可能可以改变原变量的引用都是放大权限。一般原变量也是引用就是可以被允许改变。
- 一个变量可以有多个别名
- 引用一旦引用了一个实体,就不能再引用其它的实体,用C语言的话说,就是这个引用的地址从此和所引用的实体绑定(相同),不会再发生变化了,从一而终。
int main() {
int a = 1;
int& b = a;
int& c = b;
int& d = b;//可以引用多个别名
cout << &a << endl;
cout << &b << endl;
cout << &c << endl;
cout << &d << endl;
return 0;
}
地址都相同
引用使用场景
🥛1.传参
void Swap(int& left, int& right)
{
int temp = left;
left = right;
right = temp;
}
传参在任何情况下都可以使用
🥛2.做返回值
int& Count()
{
static int n = 0;
n++;
// ...
return n;
}
做返回值使用情况:返回值在出作用域后仍存在不销毁
为什么呢?🤔
我们上述知道,引用就是和原来的变量绑定在一起(相同地址),所以二者是属于同一本源的,一方改变,另一方也会发生变化,我们知道,函数结束进程后,该函数创建的栈帧就会销毁,里面所定义的变量自然也就不复存在了,这些变量就可能会变成随机值,而如果用引用返回,那么该引用值可能就会发生改变。
所以我们要规避这种风险情况。
引用作为传参和返回值的相对于值的效率对比
结论:引用作为传参和返回值的效率高于值。
也好理解,传值操作只是实参的一份临时拷贝,它在创建栈帧的时候,还要专门创建临时变量,这无疑大大降低了效率。
👉🏻内联函数
概念
以inline修饰的函数叫做内联函数,编译时C++编译器会在调用内联函数的地方展开,没有函数调用建立栈帧的开销,内联函数提升程序运行的效率。
如果在函数前增加inline关键字将其改成内联函数,在编译期间编译器会用函数体替换函数的调用。
在Debug模式下查看汇编情况
这里只展开了函数功能,并未调用函数创建新的栈帧空间。
内联函数的特性以及使用注意点
🫐1.内敛函数本质是以空间换时间
因为它是以函数体代替了函数调用,如果函数体本身大或者展开次数多的话,代码量就会非常大,会导致目标文件很大
🫐2.内联说明只是对编译器的建议,是否采用取决于编译器
如果函数体过大,编译器会忽略内联声明,仍采用函数调用的方法。
所以我们一般只对调用频繁、函数体小、流程直接不递归的函数进行内联说明,以提高程序运行效率
🫐3.inline不建议声明和定义分离,分离会导致链接错误
这里的声明和定义分离,是指声明和定义分别位于不同的.cpp文件当中。
因为内联函数调用是直接展开,没有地址,链接过程时,内联函数的地址并未进入符号表,找不到地址,自然会出现链接错误。
宏的一些问题和内联函数的关系
🍳宏的优缺点?
优点:
1.增强代码的复用性。
2.提高性能。
缺点:
1.不方便调试宏。(因为预编译阶段进行了替换)
2.导致代码可读性差,可维护性差,容易误用。
3.没有类型安全的检查 。
🍳C++有哪些技术替代宏?
- 常量定义 换用const enum
- 短小函数定义 换用内联函数
👉🏻auto关键字
概念
用auto修饰的变量会自动匹配对应的类型。
int a = 0;
auto b = a;
此时auto会自动匹配,b的类型为int
使用auto定义变量时必须对其进行初始化,在编译阶段编译器需要根据初始化表达式来推导auto的实际类型。因此auto并非是一种“类型”的声明,而是一个类型声明时的“占位符”,编译器在编译期会将auto替换为变量实际的类型。
auto使用注意点
- 用auto声明指针类型时,用auto和auto*没有任何区别,但用auto声明引用类型时则必须加&
- 当在同一行声明多个变量时,这些变量必须是相同的类型,否则编译器将会报错,因为编译器实际只对第一个类型进行推导,然后用推导出来的类型定义其他变量。
void TestAuto()
{
auto a = 1, b = 2;
auto c = 3, d = 4.0; // 该行代码会编译失败,因为c和d的初始化表达式类型不同
}
auto不能应用的场景
1.auto不能用作函数参数,因为编译器无法对函数参数的原类型进行推导
2.auto不能用作声明数组
👉🏻基于范围的for循环(C++11)
基于范围的for循环只要给出数组名,就会自动遍历数组。
int main()
{
int arr[] = { 1,2,3,4,5,6,7,8,9,10 };
for (auto x : arr)
{
cout << x << " ";
}
return 0;
}
注意:for循环迭代的范围必须是确定的
👉🏻指针空值nullptr(C++11)
nullptr既代表0也是((void*)0)
👉🏻getline函数
在C++的string库中,getline()函数是用于从输入流中读取一行文本的函数。
getline()函数的基本语法如下:
getline(istream& input, string& str, char delimiter='\n');
其中,参数说明如下:
input
:输入流对象,表示从哪个输入流中读取数据。可以是cin
、文件流对象等。str
:字符串对象,用于存储读取到的文本行。delimiter
(可选):字符型参数,表示行的结束符,默认为换行符\n
。
使用getline()函数的示例代码如下:
#include <iostream>
#include <string>
int main() {
std::string line;
// 从标准输入中读取一行文本
std::cout << "请输入一行文本:";
std::getline(std::cin, line);
std::cout << "你输入的文本是:" << line << std::endl;
return 0;
}
运行以上示例代码时,程序会等待用户在终端输入一行文本,用户按下回车键后,getline()函数会将输入的文本存储到line
字符串中,并输出到终端上。
需要注意的是,getline()函数会读取一行文本,包括行末的换行符(如果有的话),但不会将换行符保存到字符串中。如果不指定delimiter
参数,默认的行结束符是换行符\n
,但也可以通过指定其他字符作为结束符来读取特定格式的文本行。
如上便是本期的所有内容了,如果喜欢并觉得有帮助的话,希望可以博个点赞+收藏+关注🌹🌹🌹❤️ 🧡 💛,学海无涯苦作舟,愿与君一起共勉成长