目录
一. C++关键字
二. 命名空间 - namespace
1. 标准写法
2. 特性
2. 注意事项
三. C++ 输入 & 输出
四. 缺省参数
应用场景
五. 函数重载
C++支持函数重载的原理 - 名字修饰(name Mangling)
六. 引用
1. 使用场景
1.1 做参数
1.2 做返回值
1.3 总结:
1.4 应用场景
2. 常引用问题
3. 引用和指针的区别
七. 内联函数
注意:
八. auto 关键字(C++ 11)
1. auto 使用细则
2. auto 不能推导的场景
九. 范围 for(C++ 11)
1. 语法
2. 使用条件
十. 空指针 nullptr(C++ 11)
整个 C++ 入门都是在补充 C 语言的不足
一. C++关键字
二. 命名空间 - namespace
C语言中,全局命名冲突:1. 我们跟库冲突 2. 我们互相之间的冲突
#include <stdio.h>
#include <stdlib.h>int rand = 0;int main()
{printf("%d\n", rand);return 0;
}
这段代码,删掉第二行可以编译通过。加上第二行无法编译通过:“rand”: 重定义;以前的定义是“函数”
这里是和库里生成随机数的 rand 冲突了
包含#include <stdlib.h>,在预处理阶段拷贝。==> 全局已经有了 rand 想定义 rand,要定义在域里
#include <stdio.h>
#include <stdlib.h>namespace bit
{int rand = 1;
} // 没有 ;int main()
{printf("%d\n", bit::rand);printf("%p\n", rand);return 0;
}
C语言无法解决这个问题,C++的命名空间可以
namespace 是关键字,定义一个域。想让上面的程序不冲突,可以用域进行隔离
类域、命名空间域、局部域、全局域
int a = 0;int main()
{int a = 1;printf("%d\n", a); // 1 局部优先return 0;
}
同一个域里不能定义同一个变量。这两个 a 可以同时存在
// 访问全局的 a
int a = 0;int main()
{int a = 1;printf("%d\n", a); // 1// ::域作用限定符,在 ::左边的域访问。左边是空白,代表去全局域访问printf("%d\n", ::a); // 0return 0;
}
1. 标准写法
3个 a 在不同域,可同时存在
当这几个域同时存在时,变量搜索的范围优先级:局部域 > 全局域 > 展开了命名空间域 or 指定访问命名空间域
展开:编译时是否去命名空间中搜索
namespace bit // 定义一个域叫 bit
{int a = 1; // 把变量放进域里面
}using namespace bit; // 展开了命名空间int main()
{printf("%d\n", a); // 1return 0;
}
// 3个 a 全访问
int a = 0;namespace bit
{int a = 1;
}int main()
{int a = 2;printf("%d\n", a); // 2printf("%d\n", ::a); // 0printf("%d\n", bit::a); // 1 指定访问命名空间域return 0;
}
int a = 0;namespace bit
{int a = 1;
}using namespace bit; // 展开了命名空间int main()
{printf("%d\n", a);return 0;
}
展开 ==> 暴露在全局。
此时这俩 a 不能同时存在
命名空间的意义是防止和别人冲突,我把自己围起来,指定访问
展开就又冲突了,不要轻易的用 using namespace bit ; 这句话
2. 特性
命名空间里可以定义:变量、函数、类型(结构体)、嵌套命名空间
namespace N1
{int a = 0;int b;int Add(int left, int right){return left + right;}struct Node{struct Node* next;int val;};namespace N2{int a = 1; // 俩 a 不是同一个int c;int Sub(int left, int right){return left - right;}}
}int main()
{printf("%d\n", N1::a);printf("%d\n", N1::N2::a);printf("%d\n", N1::Add(1, 2));return 0;
}
同一个工程中允许存在多个相同名称的命名空间,编译器最后会合成同一个命名空间中。
// Queue.h
#pragma once
namespace bit
{int y = 1;
}// Stack.h
#pragma once
namespace bit
{int x = 0;
}// Test.cpp
#include "Stack.h"
#include "Queue.h"namespace bit
{int rand = 2;
}int main()
{printf("%d\n, bit::x); // 0printf("%d\n, bit::y); // 1printf("%d\n, bit::rand); // 2
}
同一个命名空间如果冲突,比如:
// Queue.h
#pragma once
namespace bit
{int x = 3;
}
被合并到同一个命名空间,不能有同名变量,报错
怎么解决? 嵌套命名空间
2. 注意事项
#include <iostream>
#include <vector>
#include <list>int main()
{cout << "hello world" << endl; // 报错list<int> lt; // 报错return 0;
}
编不过。C++标准库(iostream, cout就是标准库里的东西)、STL都在 std 里面
包含了3个头文件,在预处理阶段会展开。会把3个头文件里定义的库的东西都放出来。库里面的东西又都是分在 std这个命名空间域 里的
包含了头文件,eg:这里定义 list
#include <iostream>
#include <vector>
#include <list>namespace std
{class list{ };
}int main()
{cout << "hello world" << endl; // 报错list<int> lt; // 报错return 0;
}
默认不会去命名空间里面搜索
正确写法:
#include <iostream>
#include <vector>
#include <list>int main()
{std::cout << "hello world" << std::endl;std::cout << "hello world" << std::endl;std::cout << "hello world" << std::endl;std::cout << "hello world" << std::endl;std::list<int> lt;return 0;
}
这样每次都加 std:: 很烦? 一把展开
#include <iostream>
#include <vector>
#include <list>using namespace std;int main()
{cout << "hello world" << endl;std::cout << "hello world" << std::endl; // 加不加都可以list<int> lt;return 0;
}
直接展开会有风险,我们定义如果跟库重名,就报错。
项目里不要展开,项目里推荐指定访问
展开某个:把常用的展开
#include <iostream>
#include <vector>
#include <list>using std::cout;
using std::endl;int main()
{cout << "hello world" << endl;cout << "hello world" << endl;cout << "hello world" << endl;cout << "hello world" << endl;std::list<int> lt;return 0;
}
三. C++ 输入 & 输出
" hello world ":IO流 " hello world " 流向 cout
#include <iostream>using std::cout;
using std::endl;int main()
{int x = 10;cout << "hello world" << x << '\n' << endl;cout << "hello world" << endl;return 0;
}
相比 printf,cout 可一行连续插入多个变量、常量; printf 要指定类型,cout 自动识别类型
int main()
{int x = 10;double d = 11.11;cout << x << " " << d << endl;std::cin >> x >> d;cout << x << " " << d << endl;printf("%d %.5f\n", x, d);return 0;
}
自动识别,精度丢失。想控制精度很难。喜欢用 C语言 控制精度
四. 缺省参数
void Func(int a = 0)
{cout << a << endl;
}
int main()
{Func(); // 0Func(10);// 10return 0;
}
没有传参时,使用参数的默认值 传参时,使用指定的实参
注意:1. 函数传参,从左往右接收
2. 半缺省,必须从右往左缺省。且此时必须传参。不能指定传参
3. 缺省参数不能在函数声明和定义中同时出现。声明中给缺省参数,定义中不给
全缺省:
void Func(int a = 10, int b = 20, int c = 30)
{cout << "a = " << a << endl;cout << "b = " << b << endl;cout << "c = " << c << endl;
}int main()
{Func(); // 10 20 30Func(1); // 1 20 30Func(1, 2); // 1 2 30Func(1, 2, 3); // 1 2 3return 0;
}
半缺省 - 必须从右往左缺省:
void Func(int a, int b = 20, int c = 30)
{cout << "a = " << a << endl;cout << "b = " << b << endl;cout << "c = " << c << endl;
}int main()
{// Func(); 错误,此时必须传参Func(1); // 1 20 30Func(1, 2); // 1 2 30Func(1, 2, 3); // 1 2 3return 0;
}
应用场景
struct Stack
{int* a;int top;int capacity;
};void StackInit(struct Stack* pst)
{pst->a = (int*)malloc(sizeof(int) * 4);if (pst->a == NULL){perror("malloc fail");return;}pst->top = 0;pst->capacity = 4;
}int main()
{struct Stack st;StackInit(&st);// 插入100个数据return 0;
}
C语言的栈中:我明知要插入 100 个数据,还初始化容量为4,让他慢慢扩容。扩容是有消耗的
用 C语言 优化后:
void StackInit(struct Stack* pst, int defaultCapacity) // 默认初始化容量
{pst->a = (int*)malloc(sizeof(int) * 4);if (pst->a == NULL){perror("malloc fail");return;}pst->top = 0;pst->capacity = defaultCapacity;
}int main()
{struct Stack st;StackInit(&st, 100); // 插入100个数据return 0;
}
如果不知道要插入多少数据呢? 初始化过大浪费;过小不断扩,消耗也很大
C++ 缺省参数优化:更灵活
void StackInit(struct Stack* pst, int defaultCapacity = 4)// 开始不要给太大
{ // 扩容就扩容,至少不要浪费pst->a = (int*)malloc(sizeof(int) * 4);if (pst->a == NULL){perror("malloc fail");return;}pst->top = 0;pst->capacity = defaultCapacity;
}int main()
{struct Stack st1;StackInit(&st1, 100); // 想控制也可以传struct Stack st2;StackInit(&st2); // 不知道插入多少个数据,你也不要传了return 0;
}
五. 函数重载
函数重载:C++允许在同一作用域中声明几个功能类似的同名函数,这些同名函数的形参列表(参数个数 或 类型或 类型顺序)不同,常用来处理实现功能类似数据类型不同的问题。注:返回值没有要求
1. 参数类型不同:
int Add(int left, int right)
{cout << "int Add(int left, int right)" << endl;return left + right;
}double Add(double left, double right)
{cout << "double Add(double left, double right)" << endl;return left + right;
}int main()
{cout << Add(1, 2) << endl; // 3cout << Add(1.1, 2.2) << endl; // 3.3return 0;
}
2. 参数类型顺序不同
void f(int a, char b)
{cout << "f(int a,char b)" << endl;
}void f(char b, int a)
{cout << "f(char b, int a)" << endl;
}int main()
{f(1, 'a');f('a', 1);return 0;
}
3. 参数个数不同
void f()
{cout << "f()" << endl;
}void f(int a)
{cout << "f(int a)" << endl;
}int main()
{f();f(10);return 0;
}
问题代码:是否构成重载? 构成 问题:无参调用存在歧义
无参和全缺省,语法上构成重载;现实中没人会把他俩写成重载
void f() // 无参
{cout << "f()" << endl;
}void f(int a = 0) // 全缺省
{cout << "f(int a)" << endl;
}int main()
{f(); // 报错:“f”: 对重载函数的调用不明确return 0;
}
C++支持函数重载的原理 - 名字修饰(name Mangling)
C语言中,符号表的名字直接用的函数名 Linux 下 gcc
C++中,函数符号表,函数修饰后变成:_Z+函数长度 +函数名+类型首字母 Linux 下 g++
不同的编译器,符号表中,对函数名修饰的结果是不一样的
六. 引用
指针:
引用不是新定义一个变量,而是给已存在变量取了一个别名,编译器不会为引用变量开辟内存空间,它和它引用的变量共用同一块内存空间。
eg:李逵,铁牛,黑旋风
1. 引用在定义时必须初始化
2. 一个变量可以有多个引用
3. 引用一旦引用一个实体,再不能引用其他实体
int main()
{// 1.一个变量可以有多个引用int a = 0;int& b = a;int& c = b;// 2.引用在定义时必须初始化int& d; // 报错// 3.引用一旦引用一个实体,再不能引用其他实体int x = 10;c = x; // x的值赋给c,c依旧是a/b对象别名// 不是 c变成x的引用(别名)return 0;
}
int main()
{int a = 0;int& b = a;int& c = b;int& d = a;cout << &a << endl;cout << &b << endl;cout << &c << endl;cout << &d << endl;b++;d++;int x = 11;d = x;return 0;
}
1. 使用场景
1.1 做参数
1、输出型参数:形参改变要影响实参
2、提高效率:大对象,深拷贝类对象
void Swap(int& a, int& b)
{int tmp = a;a = b;b = tmp;
} // 形参是实参的别名,形参改变会影响实参void Swap(int*& a, int*& b)
{int* tmp = a;a = b;b = tmp;
}typedef struct ListNode
{int val;struct ListNode* next;
}LTNode, * PLTNode; // 下面有解析//void ListPushBack(struct ListNode*& phead, int x)
//void ListPushBack(LTNode*& phead, int x)
void ListPushBack(PLTNode& phead, int x)
{// phead = newnode; 单链表中有一步要改变 phead ,因此 C语言 中要传2级指针
}int main()
{int x = 0, y = 1;Swap(x, y);cout << x << " " << y << endl;int* px = &x, * py = &y;cout << px << " " << py << endl;Swap(px, py);cout << px << " " << py << endl;PLTNode plist = NULL;ListPushBack(plist, 1);return 0;
}
LTNode 是 struct ListNode 重命名后的
struct ListNode* 取别名为 PLTNode
1.2 做返回值
1、提高效率:大对象,深拷贝类对象
2、修改返回值+获取返回值
C语言中:
// 代码 1
int Count()
{int n = 0;n++;// ...return n;
}
int main()
{int ret = Count();return 0;
}
传值返回,n 不是直接传给 ret
这里 n 在局部域,在栈帧里。出作用域,栈帧销毁,不能用 n 作为返回值
编译器会自动生成临时变量(数据小,可能是寄存器,4/8 字节)n -> copy -> 临时变量 ->copy -> ret
// 代码 2
int Count()
{static int n = 0;n++;// ...return n;
}
int main()
{int ret = Count();return 0;
}
这里 n 在静态区,不受栈帧的影响,会不会生成临时变量?
会,编译器不做特殊处理 n -> copy -> 临时变量 ->copy -> ret
传值返回生成临时变量,是因为传值返回。跟出了作用域 n 还在不在无关。
大对象、深拷贝类对象对效率影响会很大
能不能不再生成临时变量?—— 引用做返回值
静态对象引用返回:
int& Count()
{static int n = 0;n++;// ...return n;
}
int main()
{int ret = Count();return 0;
}
Count 栈帧的销毁不影响 n
上面静态的,用引用返回没有问题。但不是都能用引用返回:返回局部对象的引用很危险
危险1:
int& Count()
{int n = 0;n++;// ...return n;
}int main()
{int ret = Count(); // 1 / 随机值cout << ret << endl;return 0;
}
返回的是 n 的别名。n 和 ret 是拷贝关系。返回值初始化给 ret
这里栈帧销毁跟 ret 没关系,后面再调用函数,不会影响 ret
这里打印的 ret 是不确定的
如果 Count 函数结束,栈帧销毁,没有清理栈帧空间,那么 ret 结果侥幸是正确的
如果 Count 函数结束,栈帧销毁,清理栈帧,ret 结果是随机值
cout << ret << endl ; 访问的是一块已经销毁的空间上的变量
危险2:
int& Count(int x)
{int n = x;n++;// ...return n;
}int main()
{int& ret = Count(10);cout << ret << endl; // 11 / 随机值Count(20); // 调同一个函数,栈帧是同样大的。n 还在这块空间,给20,就被覆盖成20cout << ret << endl; // 21 / 随机值return 0;
}
这里返回的是 n 的别名。ret 是(n 的别名)的别名。《==》ret 是 n 的别名
地址一样,是同一块空间
如果调用的是任意的其他函数:危险2:
int& Count(int x)
{int n = x;n++;// ...return n;
}int main()
{int& ret = Count(10);cout << ret << endl;printf("八嘎\n");cout << ret << endl;return 0;
}
空间被覆盖了
ret 能访问,但是没有保障。访问的是一块已经销毁的空间上的变量《==》野指针,只是没有报错
1.3 总结:
1. 基本任何场景都可以用引用传参
2. 谨慎用引用做返回值。出了函数作用域,对象不在了,就不能用引用返回,还在就可以用引用返回
1.4 应用场景
C语言
struct SeqList
{int a[100];size_t size;
};int SLGet(SeqList* ps, int pos)
{assert(pos < 100 && pos >= 0);return ps->a[pos];
}void SLModify(SeqList* ps, int pos, int x)
{assert(pos < 100 && pos >= 0);ps->a[pos] = x;
}int main()
{SeqList s;SLModify(&s, 0, 1); // 给第0个位置赋值为1cout << SLGet(&s, 0) << endl; // 获取第0个位置的数据int ret1 = SLGet(&s, 0);SLModify(&s, 0, ret1+5); // 给第0个位置的数据+5return 0;
}
C++
int& SLAt(SeqList* ps, int pos)
{assert(pos < 100 && pos >= 0);return ps->a[pos];
}int main()
{SeqList s;SLAt(&s, 0) = 1; // 给第0个位置赋值为1cout << SLAt(&s, 0) << endl; // 获取第0个位置的数据SLAt(&s, 0) += 5; // 给第0个位置的数据+5return 0;
}
出了作用域,返回对象 ps->a[pos] 还在,因为是结构体里面的数组,不是局部变量,是外面传过来的
更 C++
int& SLAt(SeqList& s, int pos)
{assert(pos < 100 && pos >= 0);return s.a[pos];
}int main()
{SeqList s;SLAt(s, 0) = 1; // 给第0个位置赋值为1cout << SLAt(s, 0) << endl; // 获取第0个位置的数据SLAt(s, 0) += 5; // 给第0个位置的数据+5return 0;
}
2. 常引用问题
int main()
{const int a = 0;int& b = a; // 错。引用过程中,权限不能放大。b的改变会影响aconst int c = 0;int d = c; // 对。c拷贝给d,没有放大权限。因为d的改变不影响cint x = 0;int& y = x; // 对。引用过程中,权限可以平移const int& z = x; // 对。引用过程中,权限可以缩小++z; // 错 下面解释++x; // 对 下面解释int& m = 10; // 错const int& m = 10; // 对。权限的平移。10是不能修改的,m变成10的别名return 0;
}
在梁山叫李逵(x)、铁牛(y),下山只叫黑旋风(z) ==》 权限缩小了
李逵(x)、铁牛(y)可以吃肉、喝酒 黑旋风(z)只能吃肉,不能喝酒。
++z 相当于黑旋风(z)喝酒,错
++x 相当于李逵(x)、铁牛(y)喝酒 ==》则黑旋风(z)也喝了酒,黑旋风(z)的值也会发生改变
只缩小 z 作为别名的权限(只读),++z 错
用 z 不能改变,用 x y 能改变,++x 对。x y 的改变就是 z 的改变
int main()
{double dd = 11.11;int ii = dd; // 对。隐式类型转换int& rii = dd; // 错。const int& rii = dd; // 对。return 0;
}
发生类型转换(强制、隐式、类型提升、截断)时,中间会产生临时变量《==》传值返回
操作符两边的类型不同,会发生整型提升,且不能改变变量的数据
所以会生成临时变量(被提升的)。用临时变量与没有进行提升的运算
临时变量具有常性(相当于让 const 修饰了)
所以 int& rii = dd ; 错的原因是权限的放大,不是因为类型不同
int func1()
{static int x = 0;return x;
}int main()
{int ret1 = func1(); // 对 拷贝int& ret2 = func1(); // 错 权限的放大const int& ret3 = func1(); // 对 权限的平移return 0;
}
int& func2()
{static int x = 0;return x; // 没产生临时变量,返回的是 x的别名
}int main()
{int& ret2 = func2(); // 对 ret2给别名取别名,还是 x的别名 权限的平移const int& ret3 = func2(); // 对 权限缩小return 0;
}
3. 引用和指针的区别
底层汇编指令实现的角度看,引用是类似指针的方式实现的,要开空间。
但学习过程中,通常不认为引用开空间
- 引用概念上定义一个变量的别名,指针存储一个变量地址
- 引用在定义时必须初始化,指针没有要求
- 引用在初始化时引用一个实体后,就不能再引用其他实体,而指针可以在任何时候指向任何 一个同类型实体
- 没有NULL引用,但有NULL指针
- 在sizeof中含义不同:引用结果为引用类型的大小,但指针始终是地址空间所占字节个数(32 位平台下占4个字节)
- 引用自加即引用的实体增加1,指针自加即指针向后偏移一个类型的大小
- 有多级指针,但是没有多级引用
- 访问实体方式不同,指针需要显式解引用,引用编译器自己处理
- 引用比指针使用起来相对更安全
七. 内联函数
C语言中,如果一个函数要频繁调用,每次调用都会建立栈帧,消耗大
int Add(int x, int y)
{return (x + y) * 10;
}int main()
{for (int i = 0; i < 10000; i++){cout << Add(i, i + 1) << endl;}return 0;
}
简化:宏函数替换
#define ADD(x, y) (((x) + (y)) * 10)//#define Add(int x, int y) return (x+y);
//#define Add(x, y) x+y
//#define Add(x, y) (x+y)
//#define Add(x, y) ((x)+(y))// if (Add(10, 20))
// {
//
// }
//
// Add(10, 20) * 20; // x+y
//
// int a = 1, b = 2;
// Add(a | b, a & b); // (a | b + a & b) // (x+y)
优点-- 不需要建立栈帧,提高调用效率;复用性
缺点-- 复杂,容易出错、可读性差;不能调试(预处理阶段替换了);没有类型安全的检查
C++ 内联函数:
inline int Add(int x, int y)
{return (x + y) * 10;
}int main()
{for (int i = 0; i < 10000; i++){cout << Add(i, i + 1) << endl;}return 0;
}
关键字 inline 修饰。编译时C++编译器会在调用内联函数的地方展开(用函数体替换函数的调用)。不建立栈帧,提升效率
inline 很好,但不能乱用。只适用短小的频繁调用的函数。太长的函数会导致代码膨胀
默认debug模式下,inline不会起作用,否则不方便调试了
inline对于编译器仅仅只是一个建议,最终是否成为inline,编译器自己决定
像类似函数就加了inline也会被否决掉:1. 比较长的函数 2. 递归函数
注意:
内联函数不能声明、定义分离。他认为用的地方都会展开,内联函数是不需要被 call 的,所以没有地址,不会进符号表。
分离会导致链接问题 直接定义在 .h
// Func.h
#include <iostream>
using namespace std;inline void f(int i);// Func.cpp
#include "Func.h"void f(int i)
{cout << i << endl;
}// Test.cpp
#include "Func.h"int main()
{f(10);return 0;
}
链接错误:Test.obj : error LNK2019: 无法解析的外部符号 "void __cdecl f(int)" (?f@@YAXH@Z),函数 _main 中引用了该符号
正确写法:
// Func.h
#include <iostream>
using namespace std;inline void f(int i)
{cout << i << endl;
}// Test.cpp
#include "Func.h"int main()
{f(10);return 0;
}
八. auto 关键字(C++ 11)
auto:长类型替换
int main()
{int a = 0;int b = a;auto c = a; // 根据右边的表达式自动推导c的类型auto d = 1 + 1.11; // 根据右边的表达式自动推导d的类型cout << typeid(c).name() << endl;cout << typeid(d).name() << endl;return 0;
}
#include <iostream>
#include <map>
#include <string>
#include <vector>
using namespace std;int main()
{vector<int> v;// 类型很长//vector<int>::iterator it = v.begin();// 等价于auto it = v.begin();std::map<std::string, std::string> dict;//std::map<std::string, std::string>::iterator dit = dict.begin();// 等价于auto dit = dict.begin();return 0;
}
1. auto 使用细则
auto 定义变量必须初始化。编译阶段,编译器根据初始化表达式来推导 auto 的实际类型。因此auto并非是一种“类型”的声明,而是一个类型声明时的“占位符”,编译器在编译期会将auto替换为变量实际的类型
(1)auto、指针、引用合起来用
int main()
{int x = 10;auto a = &x; // 根据右边推出来auto* b = &x; // 指定必须是指针//auto* f =10; // 报错auto& c = x; // 指定必须是引用return 0;
}
(2)同一行定义多个变量
void TestAuto()
{auto a = 1, b = 2; auto c = 3, d = 4.0; // 该行代码会编译失败,因为c和d的初始化表达式类型不同
}
在同一行声明多个变量时,这些变量必须是相同的类型
因为编译 器实际只对第一个类型进行推导,然后用推导出来的类型定义其他变量
2. auto 不能推导的场景
(1)auto 不能作为函数的参数
// 此处代码编译失败,auto不能作为形参类型,因为编译器无法对a的实际类型进行推导
void TestAuto(auto a) // 栈帧都不知道开多大
{ }
(2)auto不能直接用来声明数组
void TestAuto()
{int a[] = {1,2,3};auto b[] = {4,5,6}; // 报错
}
(3)为了避免与 C++98中的 auto 发生混淆,C++11只保留了 auto 作为类型指示符的用法
(4)auto 优势用法是跟 C++11 提供的新式 for 循环,还有 lambda 表达式等 配合使用
九. 范围 for(C++ 11)
适用于数组、容器
1. 语法
for ( 范围内用于迭代的变量 :被迭代的范围 )
与普通循环类似,可以用continue来结束循环,也可以用break来跳出整个循环
C语言:
int main()
{int arr[] = { 1, 2, 3, 4, 5 };for (int i = 0; i < sizeof(arr) / sizeof(arr[0]); ++i)arr[i] *= 2; // 写for (int* p = arr; p < arr + sizeof(arr) / sizeof(arr[0]); ++p)cout << *p << " "; // 读cout << endl;return 0;
}
C++:
int main()
{int arr[] = { 1, 2, 3, 4, 5 };// 依次取数组中数据赋值(拷贝)给e 自动迭代,自动判断结束// for (int e : arr) double的数组就得改for (auto e : arr){cout << e << " ";}cout << endl;// 修改数据for (auto& e : arr){e *= 2;}for (auto e : arr){cout << e << " ";}cout << endl;return 0;
}
修改数据,不能写成:for (auto e : arr)
自动推导 e 的类型,依次取数组中数据赋值(拷贝)给 e。e 的改变不会影响数组的内容
且不能配指针,依次取数据,数据不能给指针
2. 使用条件
1. for循环迭代的范围必须是确定的
对于数组而言,就是数组中第一个元素和最后一个元素的范围
对于类而言,应该提供 begin 和 end 的方法,begin 和 end 就是for循环迭代的范围
void TestFor(int arr[]) // 这个 arr是指针,不是数组
{for(auto& e : arr)cout<< e <<endl;
}
2. 迭代的对象要实现++和==的操作
十. 空指针 nullptr(C++ 11)
(1) 使用 nullptr 表示空指针时,不需要包含头文件,因为 nullptr 是 C++11 作为新关键字引入的
(2) 在C++11中,sizeof(nullptr) 与 sizeof((void*)0)所占的字节数相同 nullptr <==> (void*)0
(3) 以后空指针用 nullptr
C++98中,NULL 是宏。在传统的C头文件(stddef.h)中,可以看到如下代码:
void f(int) // C/C++编译器没有规定必须形参接收
{cout << "f(int)" << endl;
}void f(int*)
{cout << "f(int*)" << endl;
}int main()
{f(0);f(NULL); // 本意想调 int* 但失误了f((int*)NULL);f(nullptr);return 0;
}
本篇的分享就到这里了,感谢观看,如果对你有帮助,别忘了点赞+收藏+关注。
小编会以自己学习过程中遇到的问题为素材,持续为您推送文章