【C++初阶】一、C++入门基础(详细总结) 您所在的位置:网站首页 缺省值的作用 【C++初阶】一、C++入门基础(详细总结)

【C++初阶】一、C++入门基础(详细总结)

2023-03-24 08:12| 来源: 网络整理| 查看: 265

文章目录 一、什么是C++二、C++关键字(C++98)三、命名空间3.1命名空间定义1.命名空间的普通定义2.命名空间可以嵌套定义3. 同一个工程中允许存在多个相同名称的命名空间,编译器最后会合成同一个命名空间中 3.2命名空间的使用1.加命名空间名称及作用域限定符2.使用using namespace 命名空间名称引入3.使用using将命名空间中成员引入 四、C++输入与输出五、缺省参数缺省参数分类1.全缺省参数2.半缺省参数 六、函数重载6.1函数重载的原理 七、引用7.1 引用的特性1.引用在定义时必须初始化2.一个变量可以有多个引用3.引用一旦引用了一个实体,就不能再引用其他实体 7.2 常引用7.3 引用的使用场景1.引用做参数2.引用做返回值 7.4 引用和指针的区别 八、内联函数8.1 概念8.2 特性 九、auto关键字(c++11)9.1 auto的使用1.auto与指针和引用结合起来使用2.在同一行定义多个变量 9.2 auto不能推导的场景1.auto做为函数的参数2.auto不能直接用来声明数组 十、基于范围的for循环(c++11)10.1 范围for的语法10.2 范围for的使用条件1.for循环迭代的范围必须是确定的2. 迭代的对象要实现++和==的操作 十一、指针空值nullptr(C++11)C++98中的指针空值C++11中的指针空值

一、什么是C++

C++是基于C语言而产生的,它既可以进行C语言的过程化程序设计,又可以进行以抽象数据类型为特点的基于对象的程序设计,还可以进行面向对象的程序设计。

二、C++关键字(C++98)

C++总计63个关键字,其中C语言占32个关键字 在这里插入图片描述

其中画圈的是C语言的关键字。这里要注意了:false和true并不是C语言的关键字。 所以说:C++兼容C的绝大多数语言特性

三、命名空间

  在C/C++中,变量、函数和后面要学到的类都是大量存在的,这些变量、函数和类的名称将都存在于全局作用域中,可能会导致很多冲突。使用命名空间的目的是对标识符的名称进行本地化,以避免命名冲突或名字污染, namespace 关键字的出现就是针对这种问题的。

#include #include //这个头文件中包含 rand这个库函数 int rand = 0;//定义的rand这个变量,与库函数中的rand函数重名,所以命名冲突了 int main() { printf("%d\n",rand); } //这时如果进行编译,则会报错 //编译报错:error C2365: “rand”: 重定义;以前的定义是“函数” // 命名冲突问题 // 1、我们自己定义的变量、函数可能跟库里面重名冲突 // 2、进入公司项目组以后,做的项目通常比较大。多人协作,两个同事写的代码,命名冲突。 // C语言没有办法很好的解决这个问题 // CPP提出一个新语法:命名空间--关键字namespace 3.1命名空间定义

定义命名空间,需要使用到namespace关键字,后面跟命名空间的名字(随自己定义),然后接一对{} 即可,{}中即为命名空间的成员。

注意:一个命名空间就定义了一个新的作用域,命名空间中的所有内容都局限于该命名空间中

例如:将上方代码进行修正,如下:

#include #include //定义了一个叫xnh的命名空间 -- 命名空间定义的是一个:域 namespace xnh { int rand = 0; } int main() { printf("%d\n", rand);//访问的是中的rand函数,打印的是以十进制打印的该函数地址 printf("%d\n", xnh::rand);//访问的是xnh这个命名空间中的rand变量,打印显示为0 return 0; }

在上述代码中会发现,若想访问命名空间中的变量,则需要借助一个作用符 :: 这个符号叫做 域作用限定符,xnh :: rand 的意思就是,去左边这个叫xnh的域(命名空间)里面找rand这个变量。

若我们想打印全局域中的一个变量,可以如下图: 在这里插入图片描述

请注意:命名空间内的变量只能允许声明和初始化,而不能在其中进行赋值!

namespace xnh { int a;//ok int b=10;//ok //b=20;//no } 1.命名空间的普通定义 namespace xnh { // 1、命名空间中可以定义变量/函数/类型 int rand = 10; int Add(int left, int right) { return left + right; } struct Node { struct Node* next; int val; }; } //使用 int main() { xnh::rand = 20; struct xnh::Node node;//注意结构体与函数和变量的不同 xnh::Add(1, 2); return 0; } 2.命名空间可以嵌套定义 //2. 命名空间可以嵌套定义 namespace N1 { int a; int b; int Add(int left, int right) { return left + right; } namespace N2 { int c; int d; int Sub(int left, int right) { return left - right; } } } //使用 int main() { N1::a = 1; N1::N2::c = 2; N1::Add(1, 2); N1::N2::Sub(3, 4); } 3. 同一个工程中允许存在多个相同名称的命名空间,编译器最后会合成同一个命名空间中

我们在一个工程中,我们可以将函数声明和定义分开来写,如下:

//在List.h头文件中只写声明 namespace xnh { int rand; struct ListNode { //... }; void ListInit(); void ListPushBack(struct ListNode* phead, int x); } //在List.cpp源文件中写定义 namespace xnh { void ListInit() { // ... } void ListPushBack(struct ListNode* phead, int x) { //... } } //在test.cpp源文件中使用 int main() { struct xnh::ListNode ln; xnh::ListPushBack(NULL, 1); return 0; }

虽然将xnh这个命名空间分开写在了List.h和List.cpp两个文件中,但最后会合成同一个命名空间中。

3.2命名空间的使用 1.加命名空间名称及作用域限定符

在这里插入图片描述

2.使用using namespace 命名空间名称引入

using namespace 命名空间名称;

这句代码的意思就是把整个命名空间展开,这样当我们使用命名空间下的变量、函数等等就不需要加作用域限定符了,用起来方便,但隔离失效了。

日常练习,小程序,这么用可以,项目最好不要这么用。 例如: 在这里插入图片描述

会发生如下的情况: 在这里插入图片描述

这样容易造成命名冲突问题,为了解决这个问题,出现了第三种引入方法。

3.使用using将命名空间中成员引入

第三种方法就是指定展开–把常用的展开,自己在定义的时候避免跟常用重名即可 例如: 在这里插入图片描述

这种方法可以防止命名冲突的问题,因为它只引入了一部分。

四、C++输入与输出

cout :输出 cin :输入

我们初学一门语言,输出Hello world是必不可少的,C++输出方法如下:

#include using namespace std; int main() { cout头文件以及std标准命名空间。

cout:可以自动识别变量的类型

使用示例:

int a = 10; char arr[] = "abcdef"; double b = 1.11; float c = 0.0; cin>>c; cout //调用 func(10); func(); //在c语言中这样写肯定是不行的,但是在c++中有了缺省参数,如果你什么都不传,只要你前面有缺省参数的存在,就能过。 return 0; }

在这里插入图片描述

缺省参数分类 1.全缺省参数

全缺省参数就是为函数的所有参数都设置一个默认参数,例如:

void Func(int a = 10, int b = 20, int c = 30) { cout //..... } // 注意:如果声明与定义位置同时出现,恰巧两个位置提供的值不同,那编译器就无法确定到底该用那个缺省值。

3.缺省值必须是常量或者全局变量 4. C语言不支持(编译器不支持)

六、函数重载

重载的意思是具有多重含义,那么函数重载即是一个函数具有多种功能,也就是对同一个函数名的不同的“解释”。

定义: C++允许在同一作用域中声明几个功能类似的同名函数,这些同名函数的形参列表(参数个数 或 类型 或 顺序)必须不同,即参数个数,参数顺序,参数类型三者有一个不同即可。常用来处理实现功能类似数据类型不同的问题

比如:

//参数类型不同 int Add(int a, int b) { return a+b; } double Add(double a, double b) { return a+b; } //参数个数不同 int Func(int a); int Func(int a,int b); //参数顺序不同 -- 顺序不同指的是,形参类型的顺序不同 int Sub(int a, char b); int Sub(char b, int a);

注意:函数重载与函数返回值类型无关。

6.1函数重载的原理

为什么C++支持函数重载而C语言却不支持呢? 在C/C++中,一个程序要运行起来,C/C++的源文件都是需要进行预处理,编译,汇编,链接,最后生成可执行程序的。和C的源文件一样,都是源文件先单独编译生成目标文件合到一起链接成可执行程序。

实际我们的项目通常是由多个头文件和多个源文件构成,函数的声明和定义分离会使编译器在编译源文件时暂时找不到函数的地址,那么其查找函数的规则则会在链接时体现。所以将函数的声明和实现放到两个文件中,更方便观察C++和C对于函数名字修饰规则的不同。

由于Windows下vs的修饰规则过于复杂,而Linux下gcc的修饰规则简单易懂,下面我们使用了gcc演示了这个修饰后的名字。

采用C语言编译器编译后结果: 在这里插入图片描述

结论:在linux下,采用gcc编译完成后,函数名字的修饰没有发生改变。

采用C++编译器编译后结果: 在这里插入图片描述

结论:在linux下,采用g++编译完成后,函数名字的修饰发生改变,编译器将函数参数类型信息添加到修改后的名字中。

通过这里就可以理解C语言无法支持重载了,因为c++会根据函数的参数对函数名进行修饰,只要参数不同,修饰出来的名字就不一样,就支持了重载,而C对函数名却不会修饰,所以没办法支持重载,因为同名函数没办法区分。

函数名不相同,其生成的函数地址也不会相同,所以在调用的时候也不会产生冲突

我们看完 函数重载 的定义后,可能会产生一个疑问,如:

返回值不同,构成重载吗??为什么呢?? 请看下面调用函数:

//两者返回值不同 int f(int a, int b) { cout f(1, 1); f(2, 2); //这里调用会存在二义性,因为调用时不指定返回类型,所以他不知道该调用哪个函数 return 0; }

返回值不同,不构成重载原因,并不是函数名修饰规则 真正原因是调用时的二义性,无法区分, 因为调用时不指定返回值类型

七、引用

引用是C++中一个重要的语法,在后期应用非常广泛。相较于指针,它更加方便也更好理解。

定义:

引用不是新定义一个变量,而是给已存在变量取了一个别名,编译器不会为引用变量开辟内存空间,它和它引用的变量共用同一块内存空间。

通俗来讲,就是为一个变量取一个别名。就如同家人为你取一个小名,两个名字都代表着同一个人。

引用的基本使用: 类型+& +引用变量名(对象名) = 引用实体;

void func() { int a = 10; int& b = a;//为a取别名叫b b = 20; int& c = b; //为b取别名叫c c = 30; }

运行结果: 在这里插入图片描述 在这里插入图片描述 在这里插入图片描述 从这里可以看出这里分别给a取了两个别名,并且别名值的改变也会影响变量a,因为别名本身代表的就是a,同时,这三个变量的地址都是同一地址,所以证明引用实体和引用变量共用同一块内存空间。

7.1 引用的特性 1.引用在定义时必须初始化 void func() { int a = 10; //int& b; //err,定义引用必须初始化 int &b = a;//正确处理 } 2.一个变量可以有多个引用 void func() { int a = 10; int &b = a;//正确处理 int &c = a; int &d = a; } 3.引用一旦引用了一个实体,就不能再引用其他实体 int a = 10; int& b = a; int c = 20; b = c;//这里是把c的值赋给b,并不是让b变为c的别名

在这里插入图片描述

7.2 常引用

常引用的定义:被const修饰的引用就是常引用。 常引用会涉及到权限的问题,如:

//权限放大 const int a = 0; int& ra = a;//Err //权限缩小 int b = 0; const int& rb = b; //权限相同 const int c = 0; const int& rc = c;

引用const修饰的常变量时,如果引用不加const,那么就造成了权限扩大显然时不允许的。权限不允许扩大但是可以不变和缩小,在用引用做参数时,可以加const修饰防止实参被修改。

double d = 1.11; int i = d; double d = 1.11; const int& i = d;

将浮点型变量d赋值给整型变量i时会发生隐式类型转换,中间会产生一个临时变量,将d的数据截断放入临时变量中再将临时变量赋值给i。类似于不同整型数据进行比较会发生整型提升,其实就是将各自产生的临时变量进行整型提升再进行比较,这也是二者的值不会发生改变的原因。 在这里插入图片描述 这样的临时变量是个右值,具有常属性,所以只有常引用才能引用这样的临时变量。也就使得const Type&的引用类型可以引用任意类型的变量。

7.3 引用的使用场景 1.引用做参数 void Swap(int& rx, int& ry) { //传引用 int tmp = rx; rx = ry; ry = tmp; } int main() { int x = 0, y = 1; Swap(x, y); cout //传址 int tmp = *px; *px = *py; *py = tmp; } void Swap(int& rx, int& ry) { //传引用 int tmp = rx; rx = ry; ry = tmp; } int main() { int x = 0, y = 1; Swap(&x, &y);//这里调用没问题,是进行传址调用 cout int& ret = Add(1, 2); cout int& ret = Add(1, 2); cout } void TestFunc2(A& a) {} void TestRefAndValue() { A a; // 以值作为函数参数 size_t begin1 = clock(); for (size_t i = 0; i int a = 10; auto b = a; auto c = 'a'; auto d = TestAuto(); //typeid(变量).name 查看变量类型 cout auto a = 1, b = 2; auto c = 3, d = 4.0; // 该行代码会编译失败,因为c和d的初始化表达式类型不同 } 9.2 auto不能推导的场景 1.auto做为函数的参数 // 此处代码编译失败,auto不能作为形参类型,因为编译器无法对a的实际类型进行推导 void TestAuto(auto a)//err {} 2.auto不能直接用来声明数组 void TestAuto() { int a[] = {1,2,3}; auto b[] = {4,5,6};//err }

为了避免与C++98中的auto发生混淆,C++11只保留了auto作为类型指示符的用法

auto在实际中最常见的优势用法就是跟以后会讲到的C++11提供的新式for循环,还有lambda表达式等进行配合使用。

十、基于范围的for循环(c++11) 10.1 范围for的语法

对于一个有范围的集合仍需说明它的范围,这无疑是多余的,因此C++11引入范围for循环。for循环后的括号由冒号“ :”分为两部分:第一部分是范围内用于迭代的变量,第二部分则表示被迭代的范围。 :前是循环变量,后面是循环范围

//C for (int i = 0; i cout e *= 2; } 10.2 范围for的使用条件 1.for循环迭代的范围必须是确定的

对于数组而言,就是数组中第一个元素和最后一个元素的范围;对于类而言,应该提供begin和end的方法,begin和end就是for循环迭代的范围。

void TestFor(int arr[]) { for (auto e : arr) { cout cout


【本文地址】

公司简介

联系我们

今日新闻

    推荐新闻

    专题文章
      CopyRight 2018-2019 实验室设备网 版权所有