c++基础语法
c++的关键字
asm 、do 、if、 return、 try、 continue、 auto、 double、 inline、 short、 typedef、 for、 bool、 dynamic_cast、 int、 signed 、typeid、 public、 break 、else 、long 、sizeof 、typename 、throw、 case、 enum 、mutable、 static、 union、 wchar_t、 catch、 explicit、 namespace、 static_cast、 unsigned 、default、char 、export、 new、 struct 、using 、friend、 class、 extern、 operator、 switch、 virtual、 register、 const、 false 、private、 template、 void、 true、 const_cast、 float 、protected、 this 、volatile、 while 、delete 、goto、 reinterpret_cast
命名空间
背景
在多人协作、共同开发项目时,很容易出现撞名称的问题,为了解决这一问题,c++中便添加了命名空间这一概念。
出错案例
这里的rand变量与stdlib.h中的rand函数发生冲突,导致程序无法正常运行。
命名空间的定义
上面介绍了命名空间出现的背景,那么该怎么使用命名空间呢?
-
namespace 命名空间名字 再配合{}将我们想要放入命名空间的变量、函数、结构之类的包含起来
namespace test
{
int rand = 1;
}
这样我们的程序就不会报错了
命名空间要注意的点
-
命名空间是可以嵌套的
namespace test
{
int rand = 1;
namespace test1
{
int a = 0;
}
}
-
同一个工程中,可以同时出现多个相同名字的命名空间,编译器会自动将他们合并
#include<iostream>
#include<stdlib.h>
namespace test
{
int rand = 1;
namespace test1
{
int a = 0;
}
}
namespace test
{
int b = 1;
}
int main()
{
printf("%d", test::b);
return 0;
}
命名空间的使用
命名空间的使用分为3种
-
加命名空间名及作用域限定符
printf("%d", test::b);
-
使用using将命名空间某个成员引入
using test::b;
//
int main()
{
printf("%d", b);
return 0;
}
-
使用using namespace 空间名称引入
using namespace test;
//
int main()
{
printf("%d",b);
return 0;
}
c++的输入和输出
学过c语言的同学都知道怎么用c语言打出hello world,那么怎么用c++和世界问号呢?
-
简单的c++程序
#include<iostream>
using namespace std;
int main()
{
cout << "hello world" << endl;
return 0;
}
注意事项
-
使用cout标准输出对象(控制台)和cin标准输入对象(键盘)时,必须包含< iostream >头文件 以及按命名空间使用方法使用std。 -
cout和cin是全局的流对象,endl是特殊的C++符号,表示换行输出,他们都包含在包含< iostream >头文件中。 -
<<是流插入运算符,>>是流提取运算符。 -
使用C++输入输出更方便,不需要像printf/scanf输入输出时那样,需要手动控制格式。C++的输入输出可以自动识别变量类型。
缺省参数
概念
-
缺省参数是声明或定义函数时为函数的参数指定一个缺省值。在调用该函数时,如果没有指定实参则采用该形参的缺省值,否则使用指定的实参。
示例
#include<iostream>
using namespace std;
void fun(int a = 5)
{
cout << a << endl;
}
int main()
{
fun();
fun(10);
return 0;
}
可以看见,当我们不传参的时候,a是默认值,传参后就变成了参数值
注意事项
-
定义和声明中只有一个能有缺省参数,一般是声明中使用,这是防止声明中的缺省参数和定义中的缺省参数不同。 -
缺省参数只能从左向右缺省不能出现以下情况
void fun(int a = 5,int b =45)
{
cout << a << b << endl;
}
int main()
{
fun();
fun(,10);//不能出现前一个使用缺省,后一个使用参数
return 0;
}
函数的重载
概念
函数名相同,但形参的个数、形参类型的顺序、形参的类型不同的函数构成重载。重载常用于实现功能类似但数据类型不同的问题,如:交换两个整型和交换一个整型和一个浮点型。注:和函数的返回值无关,和形参名字无关。
c++支持重载的原理
-
程序的编译过程:预处理(展开头文件、宏替换、去除注释、条件编译),编译(检查代码语法、词法、语义、符号汇总、生成汇编代码),汇编(将汇编代码转换成机器语言),连接(合并段表、符号表的和并和符号表的重定位)
为什么c语言没有重载而c++有重载呢?这是因为C语言在生成符号表的时候用的是函数名,而c++用的是经过修饰的函数名。
-
c++的函数名修饰 我们执行一下下面的代码
#include<iostream>
using namespace std;
int add(int a, int b);
//{
// return a + b;
//}
int add(double c, double d);
//{
// return c + d;
//}
int main()
{
add(1, 2);
add(1.0, 2.0);
return 0;
}
我们可以看见,虽然函数名相同,但是在编译时函数名被修饰了一番,这就允许了重载的存在。而c语言它在编译的时候是根据函数名来的,未加修饰,导致编译器不知道选哪一个函数为好,便没有重载。
引用
概念
引用相当于给变量“取外号”,编译器不会为了引用变量去开辟内存空间就好比旅馆不会为了周树人去给鲁迅再开一间房
引用的写法
类型& 引用变量名 = 引用实体
int a = 0;
int& b = a;
引用的特性
-
引用在定义时必须初始化 -
一个变量可以有多个引用(就像人可以有多个外号) -
引用一旦引用一个实体,那么该引用变量再不能引用其他实体(专一的很)
常引用
引用变量我们已经会了,怎么引用常量或者常变量呢? 请看代码
const int a = 0;
const int& b = a;
-
注意:在引用过程中,权限不能放大,权限的缩小、平移是被允许的 -
发生类型转换、整型提升的时候会产生一个临时变量, 而临时变量具有常性
double a = 1.0;
const int& b = a;
引用的使用场景
-
做输出参数 当我们使用一个函数,对它进行传参时,有的参数是用来输出的,比如我们在力扣刷题时遇见的int* returnsize,它是用来告知调用它的程序某数组的返回长度用的。 -
做返回值 当我们使用引用做返回值时,我们可以直接修改返回对象。 **注:当我们使用引用做返回值时,要保证被引用的空间稳定(不会随着该函数栈帧的销毁而销毁(如在该函数内创建的临时变量),堆上的空间和全局变量通常可以使用引用返回)
指针与引用的区别
-
在传值上面,引用的效率高于指针 -
指针变量是一个变量,在内存中会开辟空间,而引用不会。 -
引用在初始化时引用一个实体之后便不能引用其他实体,而指针可以 -
引用没有空引用的说法,但又空指针。 -
在sizeof中的含义不同,引用的结果为引用类型的大小,而指针始终是地址空间所占字节个数 -
引用自增则实体加1,指针自增是向后偏移一个类型的大小 -
有多级指针,没有多级引用 -
访问实体的方式不同,指针需要显式解引用(*),而引用是编译器自己处理 -
引用鼻子真的使用更为安全
内联函数
概念
使用inline关键字修饰的函数就被称为内联函数。
特点
在使用的时候会在使用处展开而不是建立函数栈帧,有一点类似于宏。
宏的优缺点
-
优点:没有类型的严格限制、效率更高 -
缺点:容易出错、不能调试(在预编译阶段就被替换了)、没有安全的类型检查(自由不代表好)
容易出错的示例
//以加法宏为例,必须按照下面的写法,否则在a | b + a & b的时候会出错
#define Add(x,y)((x)+(y))
//使用内联函数替代
inline Add(int x,int y)
{
return x+y;
}
c++中有哪些方法可以替代宏
-
对于常量的定义,可以使用const enum -
对于短小函数的定义。可以使用内联函数
编译器眼里的内联函数
不是说我们使用inline修饰的函数就是内联函数,“inline”只能算是个建议,当函数体内的语句过多或者是使用了递归语句编译器会将该函数当成普通函数,通过建立栈帧来使用而不是展开。
适合使用内联函数的情况
-
函数语句少(如交换) -
函数体内没有递归(如前序遍历等) -
满足以上两点且频繁调用的(这样可以减少栈帧开销)
内联函数的优点和缺点
-
优点:减少栈帧开销,提高效率 -
缺点:会使目标文件过大
注意事项
-
内联函数不同于普通函数,不建议声明和定义分离,建议直接定义在.h文件中。分离会导致连接错误,因为没有建立栈帧,没有生成符号表。
auto关键字
背景
随着程序越来越复杂,我们用到的类型也越来越多,越来越复杂,这就出现了两个问题
-
类型难于拼写(感兴趣可以去看一下vector容易的类型名) -
含义不明确容易出错(比如我们用typedef定义指针) 为了解决这一问题,c++添加了auto关键字
auto的定义
auto是一个类型指示符,auto声明的变量必须由编译器在编译时期推导而得到的。
使用auto时需要注意的
-
由auto定义的变量对其初始化,编译器会在编译时根据初始化表达式来推导auto的实际类型。 -
auto在声明指针时auto* 和 auto没区别,但声明引用必须使用auto& -
在同一行对多个变量进行引用的时候,这些变量必须是同一类型,因为编译器支队第一个类型进行推导,并用推导出来的类型定义其他变量。 -
auto不能作为函数的形参 -
auto不能直接用来声明数组
int a[] = {1,2,3,4};
char a[] = {1,2,3,4};
当遇到上述代码的时候,初始化的内容是相同的,auto并不清楚该数组是什么类型
基于范围的for循环
-
背景:对于一个有范围的集合(如数组),由程序员来说明范围是多于的,有时还容易出错,于是c++引入的范围for循环
int a[10] = {1,2,3,4,5,6,7,8,9,10};
int n = 10;
//普通for循环
for(int i = 0;i < n; i++)
{
;
}
//范围for循环
for(auto i : a)
{
;
}
范围for的使用条件
-
循环迭代的范围必须是确定的 -
迭代的对象要实现++和==的操作
指针空值(NULL和nullptr)
在c头文件(stddef.h)中文章来源:https://www.toymoban.com/news/detail-578098.html
#ifndef NULL
#ifdef __cplusplus
#define NULL 0
#else
#define NULL((void *)0)
#endif
#endif
我们可以发现,在c++中,NULL是0,而不是空指针,这就造成了一些麻烦,于是便有了nullptr,纯正的空指针文章来源地址https://www.toymoban.com/news/detail-578098.html
关于nullptr
-
使用nullptr表示空指针的时候不需要头文件,因为它是作为关键字引入的 -
sizeof(nullptr)和sizeof((void*)0)所占字节数相同 -
为了提高代码的健壮性,最好使用nullptr表示空指针
到了这里,关于c++基础语法的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!