C/C++

C知识点

mian(int argc ,char ** argv);

argc:参数个数;
argv:参数指针数组。
价值就是,给应用程序传递,运行需要的参数。

printf格式输出控制

%c 输出一个字符
%% 百分号本身
%s 输出一个字符串
在格式控制符中加上#前缀即可输出前缀
%hd 、%d、%ld 以十进制、有符号的形式 short 、int、long类型的整数
%hu 、%u、%lu 以十进制、无符号的形式 short 、int、long类型的整数
%ho 、%o、%lo 以八进制、不带前缀、无符号的形式 short 、int、long类型的整数
%#ho 、%#o、%#lo 以八进制、带前缀、无符号的形式 short 、int、long类型的整数
%f、%lf 以十进制形式输出 float 、double类型的小数
%e、%E 以指数形式输出 float 类型,输出结果中的e小写(输出结果中的E大写)
%le、%lE 以指数形式输出 double 类型,输出结果中的e小写(输出结果中的E大写)
%g 对比小数的十进制形式和指数形式,以最短的方式来输出小数,让输出结果更加简练

运算符规则

1.从左往右两两运算符比较;
2.如果左边高,先计算左边,如果右边高,继续往右找相对高的进行计算;
3.如果相同,看结合性,单目运算符右结合,双目运算符左结合,三目运算符嵌套右结合,计算左结合
4.重复上述。

sizeof 和 strlen

sizeof

计算字符串、数组、类型、变量所占或应占的字节数
1.计算字符串,字符串元素个数+1;
2.计算变量,变量所占的字节数;
3.计算数组名,数组所占的空间大小;
4.在32位系统下,指针变量占4个字节;
5.计算类型,就是计算类型应占但是没有占的空间大小;
6.数组名作为函数形参时,会退化成指针变量。

strlen

从某个地址开始,到第一个\0为止,中间的字符个数,不包括\0;

字节对齐

1.结构体各成员的起始位置,相对于结构体变量的起始位置的偏移量,应该是该类型所占字节
(与pack(n)中的n取最小值)的倍数;
2.结构体变量的字节数应该是各成员变量中最大类型所占字节(与pack(n)中的n取最小值)的倍数。

static 和 extern

static

1.在全局变量前,静态全局变量,仅在本文件中使用(避免多文件的命名冲突);
2.在局部变量前,静态局部变量,只初始化一次,每次函数调用结束后,内存不释放,值仍保留;
函数再次调用时,不会为其重新分配内存;
3.在函数前,静态函数,仅在本文件中使用(避免多文件的命名冲突);

extern

1.在同一源文件中,在代码下面定义的全局变量,在上面要进行使用时,可以用extern声明,
表示引用已经定义好的全局变量;
// 全局变量先于main函数加载
2.在其他源文件中,定义了全局变量,在本文件中想要使用,可以加extern声明后使用。
3.在其他源文件中,定义了函数,在本文件中使用,可以加extern声明后使用。

大小端

和位置相关,与运算无关,故不能用移位来判断。
大端be(big endian) 高字节放到低地址;
小段le(less endian) 低字节放在低地址。

char 的两种表示

1.字符  (<string.h>)
2.单字节整数

枚举

增加代码可读性。
1.所有枚举的常量都应看成符号常量;
2.枚举默认从0开始编号;
3.定义枚举类型的变量,取值范围只能是枚举的常量。

函数指针和指针函数

函数指针:函数指针变量,接收函数首地址(函数名)
函数指针作用:做回调(call back)	
指针函数:返回指针类型的函数。

有参宏和无参宏

#define PI 3.14 // PI: 宏 (该语句定义一个常量)
定义有参宏的时候,建议给每个参数都加上括号,同时对整个宏加括号;
如果定义了一个负数的宏,一定要加上括号
注意:
1.定义宏不要加等于号,不要再后面加分号;
2.宏的定义建议使用全部大写,如果由多个单词组成,中间加下划线即可
#define PI 3.14 // 无参宏,也叫符号常量
#define S(r) ((r) * (r) * PI) // 有参宏,也叫函数式宏
#define ADD(a, b) ((a) + (b))
#define RUN_ERROR (-1)

C++知识点

头文件引用

<> 默认在系统找
"" 默认在当前文件找

几个关键字

final: 写在函数后 禁止重写,写在类后,禁止被继承
static: 全局变量:仅在本文件中使用;
        局部变量:仅在本函数中使用,全局有效;
        全局函数:仅在本文件中使用;
        成员变量:类内声明,类外定义;
        成员函数:不能使用this指针,不能访问非静态的成员变量和函数,静态只能调用静态
virtual: 虚函数和虚继承
explicit: 避免隐式传递参数给构造函数
extern C: 在c++中用c的规则编译指定代码
mutable: 常函数,匿名函数中 修改不可修改的变量值(成员变量,外部作用域变量)
inline: 内联函数关键字 
    和宏的区别:  宏一定会展开且不会进行检查,内联函数会进行语法检查并且不一定会展开
auto: c++11 新特性 自动推导变量类型
const: 常量 常函数(不能修改值,不能修改成员变量)

静态库和动态库的使用

1、指定头文件的目录(是目录不是文件)
 VS中:属性 -> c/c++ -> 常规 -> 附加目录
 添加对应的头文件路径
 
 Clion中:修改cmakeLists.txt文件
 在add_executable的前面使用include_directories指定头文件的目录
 
2、指定库的目录(是目录不是文件)
 VS中:属性 -> 链接器 -> 常规 -> 附加库目录
 添加对应库的路径
 
 Clion中:修改cmakeLists.txt文件
 在add_executable的前面使用link_directories指定库文件的目录
 
3、将对应的库链接到最终的目标文件中(实际上指定链接哪个库)
 VS中:属性 -> 链接器 -> 输入 -> 附加依赖项     (.lib文件或.a文件)
 添加对应库的文件名
 
 Clion中:修改cmakeLists.txt文件
 在add_executable的后面使用target_link_libraries指定需要链接的库文件的名字
 (其中第一个参数表示工程名,后面的参数表示需要链接的库的名字)
 
注意:动态库使用时,建议将dll文件和exe文件放在同级目录

函数的默认参数

函数的默认值(缺省)参数:
(1)缺省值或者默认值参数一般只能在函数参数列表右侧
(2)声明和实现的时候只放一个位置即可,建议放在函数声明中
(3)默认值参数不能跳,必须连续出现在函数的形参中

指针和引用的区别

指针和引用的区别:
(1)指针是占用内存的,实际就是一个地址。而引用是不占用内存的。
(2)指针可以不进行初始化,如果初始化,一般为NULL或nullptr,而引用必须初始化,
	引用只能用于变量,不能是常量。
(3)指针可以多级,而引用一般不能多级。
(4)不要返回局部变量的指针(全部类型)和引用(基本类型),有踩内存风险。

malloc和new的区别

malloc和new的区别:
(1)new/delete是C++关键字,需要编译器支持。malloc/free是库函数,需要头文件支持。
	new和delete配套使用,而malloc和free配套使用,当释放一个数组时,delete需要写成delete[]
(2)使用new操作符申请内存分配时无须指定内存块的大小,编译器会根据类型信息自行计算。
	而malloc则需要显式地指出所需内存的尺寸。
(3)new操作符内存分配成功时,返回的是对象类型的指针,类型严格与对象匹配,无须进行类型转换,
	故new是符合类型安全性的操作符。而malloc内存分配成功则是返回void * ,需要通过强制类型转换
	将void*指针转换成我们需要的类型。
(4)申请内存成功后,然后调用类型的构造函数,初始化成员变量,最后返回自定义类型指针;
	malloc是库函数,只能动态的申请和释放内存,无法强制要求其做自定义类型对象构造工作。
(5)new内存分配失败时,会抛出bad_alloc异常。malloc分配内存失败时返回空指针 / NULL。

C和C++的区别

(1)C语言是面向过程的,C++是面向对象(类)的; (多了类的概念)
(2)C语言是类型匹配不严格的,而C++是严格的;
(3)C语言的全局函数是不能重载的,而C++是可以的;
(4)NULL是C语言的宏,而C++建议使用nullptr关键字;
(5)C语言的空结构体占用0个字节(一般),而C++的空结构体占用1个字节;
(6)C语言的结构体中不能定义函数,而C++的结构体是可以的;
(7)C语言申请内存释放内存一般使用malloc和free,而C++中建议一直使用new和delete关键字;
(8)C语言常量是伪常量(可以通过指针间接的修改其值),而C++是真正的常量;

命名空间

命名空间(**namespace**关键字)
作用:命名空间是C++引入的可以由用户命名的作用域(变量和函数的生命周期),
	用来处理程序中常见的同名冲突。
用法:
(1)命名空间名称 + 作用域运算符(::) + 成员
(2)声明 using namespace 命名空间名称	
(3)using 命名空间名称 :: 具体成员	
特点:
(1)命名空间可追加,全局都属于一个命名空间下
(2)命名空间可以匿名(一般不会匿名)
(3)命名空间可以嵌套
(4)命名空间可以重命名

内联函数

内联函数:

(1)使用inline关键字,
(2)或者将函数体放在类中(即函数体如果在类中,则默认是内联函数)

宏和内联函数的区别:

(1)宏是一定会展开的,而内联函数不一定会展开,取决于编译器的能力
(2)内联函数采用的是值传递,而宏定义采用的是对等替换.
(3)宏是由预处理器对宏进行替代,而内联函数是通过编译器控制来实现的。而且内联函数是真正的函数,
	只是在需要用到的时候,内联函数像宏一样的展开,所以取消了函数的参数压栈,减少了调用的开销
(4)编译器在调用一个内联函数时,会首先检查它的参数的类型,保证调用正确。然后进行一系列的相关检查,
	就像对待任何一个真正的函数一样。这样就消除了它的隐患和局限性。

构造方法

形式:与类同名,且没有返回值,即函数名前不要写返回值类型
作用:(1)创建对象 (2)初始化成员变量
种类:无参构造,有参构造,拷贝构造,移动构造
特点:
(1)类中默认提供一个无参构造和拷贝构造函数
(2)如果自己实现了有参构造后,系统将不再提供无参构造,除非自己实现
拷贝构造的作用:
	通过一个已有的对象来创建并初始化一个新的对象
拷贝构造函数的应用:
(1)类名做函数的形参
(2)类名做函数的返回值类型

析构函数

析构函数:用于撤消对象的成员函数(对象释放前被系统自动调用),做一些清理工作。
(1)先构造的后析构
(2)一个类只能有一个析构函数,析构函数不能重载
(3)析构函数无参数,无返回值
(4)如果用户没有进行定义,系统会自动生成默认析构函数
(5)如果类中使用了new分配空间,必须定义析构函数并在析构函数中用delete对分配的空间进行释放

注意:
	当类中的成员变量是指针的时候,就必须显式地写出析构函数
	类中默认提供的拷贝函数是一个浅拷贝,当我们的类成员有指针类型时,倘若我们要使用拷贝构造函数,
则必须手动实现拷贝构造(即深拷贝),以解决内存重复释放的问题。

深浅拷贝

浅拷贝(默认拷贝构造)
	2个指针指向同一内存,不产生新的资源分配,与原资源共享内存,互相影响;
深拷贝
	2个指针指向不同内存块,会产生新的资源分配,拷贝后与原资源互不影响。

成员变量初始化的顺序

(1)成员变量的初始化顺序和成员变量声明顺序相关,先声明,先初始化。
(2)先调用对象成员所属类的构造函数,再执行自身类的函数体。

空类占一个字节

	因为空类也可以实例化,每个实例在内存中都有独一无二的地址,为了达到这个目的,
编译器会给一个空类加一个字节,这样空类在实例化后在内存中就获取到一个唯一的地址。

静态成员变量的使用

使用静态成员变量的两种方式:
(1)类名::静态成员变量(建议)
(2)对象名.静态成员变量
静态成员变量是所有对象共有的,不属于某一个对象,也成为类变量

友员

友员:破坏类的封装性 降低了类的可靠性和可维护性
友元函数:
(1)类内声明和类外定义
 类内声明时:使用friend + 函数原型
 类外定义时:就是全局函数,不用加friend关键字
(2)类内直接定义,定义时在函数头前加friend关键字进行修饰(一般不建议使用类内直接定义)
友元类:
(1)友员关系不是双向的
(2)友员关系不是传递的
(3)友员关系不是继承的

重载和常函数

重载(静态多态):
	在同一作用域下,函数名相同,函数参数列表不同(个数,类型,类型顺序),与返回值类型无关,
常函数(常函数只能调用常函数,常函数中不能修改成员变量的值)也可以发生重载,则称为函数重载。
作用:达到行为标识符统一,减少程序中标识符的个数

常函数(常成员函数):
 	在成员函数头后加**const**关键字;当一个函数对成员变量不做修改时,我们可以将一个函数定义为常函数

特点:
(1)常函数对成员变量不能进行修改,但是可以在想要修改的成员变量声明前加**mutable**关键字
	来对其进行修改
(2)常函数和同形式的函数也可以发生重载
(3)常对象和常引用只能调用常函数
(4)常函数只能调用常函数

单继承

创建子类对象时,先调用父类的构造函数,再调用成员对象的构造函数,最后调用子类的构造函数
释放子类对象时,先析构子类,再析构成员对象,后析构父类

继承方式

公有继承 public 访问权限不变
私有继承 private 访问权限全变私有
受保护继承 protected 公有变成受保护,其余不变

父类私有的成员是会被子类继承的,但是不能直接访问

重定义

重定义(隐藏):不考虑虚函数的情况,如果子类中有和父类同名的函数,则父类的所有同名函数将被隐藏。
当发生重定义时,如果非要调用父类的函数;
(1)父类的引用指向子类的对象
(2)父类的指针指向子类的对象
(3)子类对象.父类名::函数名()(加上父类的作用域)

派生类不能继承的成员 (或非成员)

(1)构造函数
(2)析构函数
(3)友员
(4)运算符重载

多继承和虚继承

多继承:

当两个父类有相同的成员变量时,如果子类对象去访问这个成员变量就会出现访问不确定性,即二义性。

菱形继承:

问题:因为子类的两个父类中有两个相同的成员变量,所以子类访问时出现二义性
解决方案:采用虚继承的方式

虚继承:

在继承方式前加**virtual**关键字;
	当进行虚继承后,在当前类会产生一个虚基类指针(vbptr),子类继承父类后,实际继承的就是
这个父类的虚基类指针,因为虚基类指针指向虚基类表(vbtable),虚基类表中保存着当前虚基类指针
到虚基类成员变量的一个偏移量,所以在通过成员变量对象访问这个虚基类成员变量时,实际是通过偏移
量访问的。而子类中仅有一份虚基类成员变量,所以该成员可以被直接访问,不会产生二义性。

虚函数

在父类函数前加virtual关键字。
虚函数中的默认值:当发生多态时,只有父类的默认值起作用

动态多态

没有发生动态多态时,看左值的类型;发生多态后,看右值的类型。
(1)要有继承关系
(2)子类重写父类的虚函数
(3)父类的指针或引用指向子类对象
重写
分别位置基类和派生类中,函数名相同,参数相同,基类函数必须是虚函数。

不能成为虚函数的函数

(1)构造函数
(2)友元函数
(3)静态成员函数

纯虚函数

纯虚函数是在基类中只声明函数,没有实现,要求在派生类中定义的成员函数。
当一个函数中有纯虚函数时,这个类就是抽象类。
特点:
(1)不能实例化对象
(2)如果抽象类的子类没有重写该类的纯虚函数时,这个子类也是抽象类

为什么要定义纯虚函数:
(1)当父类的函数不需要实现一些功能时,且无需创建对象时,可以将父类的函数变为纯虚函数;
(2)定义纯虚函数,也算是将这个类变为了一个接口类,相当于给别人已经打好了一种模板,
	所以别人在继承这个抽象类时,必须重写抽象类的纯虚函数。

虚析构函数

虚析构函数:
    当子类的成员变量中有指针类型,并且父类的指针指向子类的对象时,如果delete父类的指针,
则只会调用父类的析构函数不会调用子类的析构函数,造成内存泄漏。所以应将父类的析构函数定义为
虚析构函数来解决这一问题。

纯虚析构函数:
(1)是一个纯虚函数;
(2)有纯虚析构函数的类,也是抽象类,无法实例化对象;
(3)纯虚析构函数必须在类外进行定义。

匿名函数(Lambda)

匿名函数:
	[捕获列表] (参数列表)  mutable-> 返回类型 { 函数体 };
捕获列表:
[ ]  不能引用外部变量
[=]  以常量的形式获取外部作用域内变量,只读不能修改,以拷贝的方式获取
    (加mutable关键字后可以对其进行修改,但并不会修改外部作用域内变量)
[&]  以引用的形式获取外部作用域内变量,可读可修改,以引用的方式获取
    (修改后会改变外部作用域内变量)
匿名对象的好处:
1、节省空间,在函数调用时匿名函数才会被调用,在调用结束后就会释放
2、作为函数参数进行传递(回调)

回调函数的实现:
1、全局函数
2、仿函数
3、匿名函数

C++位操作

计算1的个数

		int num = 5;
		int cnt = 0;
		while (num){
		  	++cnt;
		  	num &= num - 1;
		}
		cout << cnt << endl; // cnt = 2;

二进制低位减一

		int num = 3;
		num &= num - 1;
		cout << num << endl; // num = 2

二进制低位加一

		int num = 3;
		num |= num - 1;
		cout << num << endl; // num = 7

判断两个数是否相同

		int num1 = 2;
		int num2 = 2;
		int res = 0;
		res = num1 ^ num2;
		cout << res << endl; // res = 0;

判断三个数是否相同

		vector<int> nums; // 2 2 2
		int a = 0, b = 0;
		for (const auto& x : nums) {
		    a = (a ^ x) & ~b; // 2 0 0
		    b = (b ^ x) & ~a; // 0 2 0
		}
		cout << a << endl;

获得低位1

	int num = 6; // 0110
	cout << num & -num << endl; // 0010

C++代码

思考: 如何拷贝一个大文件 如300M -500M,并输出进度条。

void test06() {
	FILE* fp = nullptr;
	fopen_s(&fp, "D:\\software\\c++\\clion\\CLion-2022.1.2.tar.gz", "rb");
	if (fp == nullptr) {
		return;
	}
	fseek(fp, 0, SEEK_END);
	long totalSize = ftell(fp);
	rewind(fp);

	FILE* destFp = nullptr;
	fopen_s(&destFp, "D:\\software\\c++\\clion\\CLion-2022.1.2.bp.tar.gz", "wb");
	if (destFp == nullptr) {
		return;
	}

	long currRead = 0;
	while (!feof(fp)) {
		char buffer[EACH_BLOCK] = { 0 };
		fread(buffer, sizeof(char), sizeof(buffer), fp);
		currRead = ftell(fp);
		int size = currRead % EACH_BLOCK == 0 ? EACH_BLOCK : currRead % EACH_BLOCK;
		fwrite(buffer, sizeof(char), size, destFp);
		fflush(destFp);
		printf_s("总大小:%.2fM 已完成:%.2fM 当前进度:%.2f%%\n",
			(float)totalSize / 1024 / 1024,
			(float)currRead / 1024 / 1024,
			(float)currRead / totalSize * 100);
	}

	fclose(fp);
	fclose(destFp);
}

断点续传

void test08() {
	FILE* fp = nullptr;
	fopen_s(&fp, "D:\\software\\c++\\clion\\CLion-2022.1.2.tar.gz", "rb");
	if (fp == nullptr) {
		return;
	}
	// 取原文件的大小
	fseek(fp, 0, SEEK_END);
	long totalSize = ftell(fp);
	rewind(fp);

	FILE* destFp = nullptr;
	fopen_s(&destFp, "D:\\software\\c++\\clion\\CLion-2022.1.2.bp.tar.gz", "ab+");
	if (destFp == nullptr) {
		return;
	}
	// 计算目标文件目前的大小
	fseek(destFp, 0, SEEK_END);
	long writeSize = ftell(destFp);
	// 如果目标文件大于原文件,返回;否则,移动原文件光标移动目标文件大小。
	if (totalSize <= writeSize) {
		return;
	}
	else {
		fseek(fp, writeSize, SEEK_SET);
	}

	long currRead = 0;
	while (!feof(fp)) {
		char buffer[EACH_BLOCK] = { 0 };
		fread(buffer, sizeof(char), sizeof(buffer), fp);
		currRead = ftell(fp);
		int size = currRead % EACH_BLOCK == 0 ? EACH_BLOCK : currRead % EACH_BLOCK;
		fwrite(buffer, sizeof(char), size, destFp);
		// 刷新缓冲区,即立即写入文件,防止程序异常中断,缓冲区内容无法进入文件中
		fflush(destFp);
		// 进度
		printf_s("总大小:%.2fM 已完成:%.2fM 当前进度:%.2f%%\n",
			(float)totalSize / 1024 / 1024,
			(float)currRead / 1024 / 1024,
			(float)currRead / totalSize * 100);
	}

	fclose(fp);
	fclose(destFp);
}

将文件按照10MB一份的方式,分为多片/使用多片文件进行拼接成新的文件

#define ONE_FILE_SIZE 1024 * 1024 * 10
// 文件分割
void moreFileCopy()
{
    ifstream ifs("D:\\software\\Source.Insight_4.00.0124_Crack\\sourceinsight40124-setup.exe", ios::binary);
    if (ifs.is_open()) {
        ifs.seekg(0, ios::end);
        long long totalSize = ifs.tellg();
        ifs.seekg(0, ios::beg);
        char buffer[1024 * 8] = {0};
        int i = 0;
        string fileName = "D:\\software\\Source.Insight_4.00.0124_Crack\\sourceinsight40124-setup.bp.exe";
        for (int i = 0; !ifs.eof(); ++i) {
            string fileName1 = fileName;
            ofstream ofs(fileName1.insert(fileName.rfind('.'),to_string(i)), ios::binary);
            while (!ifs.eof() && ofs.tellp() < ONE_FILE_SIZE) {
                ifs.read(buffer, sizeof(buffer));
                long long len = ifs.gcount();
                ofs.write(buffer, len);
                ofs.flush();
                long long curSize = ONE_FILE_SIZE * i + ofs.tellp();
                printf_s("总大小:%.2lfMB 当前大小:%.2lfMB 进度条:%.2lf%%\n",
                            (double)totalSize / 1024 / 1024,
                            (double)curSize / 1024 / 1024,
                            (double)curSize / totalSize * 100);
            }
            ofs.close();
        }
        ifs.close();
    }
}
// 文件拼接
void moreFileCat()
{
    ofstream ofs("D:\\software\\Source.Insight_4.00.0124_Crack\\sourceinsight40124-setup.bp.exe", ios::binary);
    string fileName = "D:\\software\\Source.Insight_4.00.0124_Crack\\sourceinsight40124-setup.bp.exe";
    char buffer[1024 * 8] = {0};
    for (int i = 0; 1; ++i) {
        string fileName1 = fileName;
        ifstream ifs(fileName1.insert(fileName.rfind('.'), to_string(i)), ios::binary);
        if (!ifs.is_open()) {
            break;
        }
        ifs.seekg(0, ios::end);
        long long totalSize = ifs.tellg();
        ifs.seekg(0, ios::beg);

        while (!ifs.eof()) {
            ifs.read(buffer, sizeof(buffer));
            long long len = ifs.gcount();
            ofs.write(buffer, len);
            ofs.flush();
            long long curSize = ofs.tellp() - (long long)(ONE_FILE_SIZE * i);
            printf_s("当前文件编号:[%d] 总大小:%.2lfMB 当前大小:%.2lfMB 进度条:[%.2lf%%]\n",
                     i,
                     (double)totalSize / 1024 / 1024,
                     (double)curSize / 1024 / 1024,
                     (double)curSize / totalSize * 100);
        }
        ifs.close();
    }
    ofs.close();
}

按照分隔符截取单词

void split(const string& line, const string& delim, vector<string>& arr)
{
    int start = 0;
    int pos = 0;
    while (line.find(delim, start) != string::npos) {
        pos = line.find(delim, start);
        arr.push_back(line.substr(start, pos - start));
        start = pos + delim.size();
    }
    if (start < line.size()) {
        arr.push_back(line.substr(start));
    }
}

去除前后空格,并删除单词间多余空格

string strip(const string& line)
{
    int pos_start = line.find_first_not_of(" \r\n\t");
    int pos_end = line.find_last_not_of(" \r\n\t");
    if (pos_start == string::npos || pos_end == string::npos) {
        return line;    
        // return "";
    }
    // cout << pos_start << " " << pos_end << endl;
    string res = line.substr(pos_start, pos_end - pos_start + 1);
    // cout << res << "***" << endl;
    int pos = res.find("  ");
    while (pos != -1) {
        res.replace(pos, 2, " ");
        pos = res.find("  ", pos);
    }
    // cout << res << "***" << endl;
    return res;
}

迭代器失效场景

void test01()
{
    // 迭代器失效可能的原因:
    // (1) 删除容器的数据; (2) 容器扩容的时候.
    list<int> arr = {34, 45, 23, 65, 23, 23, 78};
    for (list<int>::iterator it = arr.begin(); it != arr.end(); ) {
        if (*it != 23) {
            ++it;
        } else {
            it = arr.erase(it); // 这里it要接erase的返回指针,防止迭代器失效
        }
    }
    for (list<int>::iterator it = arr.begin(); it != arr.end(); ++it) {
        cout << *it << " ";
    }
    cout << endl;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值