1)修饰局部变量:在函数内部使用static修饰局部变量,会使它成为静态局部变量。静态局部变量只会被初始化一次,且只有在第一次调用该函数时才会被初始化,之后每次调用该函数时都会保留上一次的值.从原来的栈区存放改为全局区。
2)修饰全局变量:在函数外部使用static修饰全局变量,会使它成为静态全局变量。静态全局变量的作用域仅限于当前文件,即只能在当前文件内被访问,不能被其他文件访问。
3)修饰成员变量:在类中使用static修饰成员变量,会使它成为静态成员变量。静态成员变量的生命周期与程序的生命周期相同,即它只会被初始化一次,且存在于所有类对象之外。静态成员变量在类内部声明,在类外部需要进行定义和初始化。
4)修饰成员函数:在类中使用static修饰成员函数,会使它成为静态成员函数。静态成员函数不依赖于任何类对象,只能访问类的静态成员变量和静态成员函数,不能访问类的非静态成员变量和非静态成员函数。静态成员函数通过类名来访问。
1)修饰变量:变量的值不能改变
2)修饰指针:左定值(常量指针)右定向(指针常量)
3)修饰成员变量:const修饰成员变量,只在某个对象的生命周期中是常量,因为类可以创建多个对象,不同的对象其const数据成员值可以不同。所以不能在类的声明中初始化const数据成员,因为类的对象还没有创建时,编译器还不知道const数据成员值是多少。所以只能在类的构造函数的初始化列表中初始化。
4)修饰成员函数:可以使用类中的所有成员变量,但是不能改变他们的值。
1)指针常量:可以改变指针指向的值,但是不能改变指针的指向
2)常量指针:可以改变指针的指向,但是不能改变指针指向的值
3)常指针常量:指针的指向和指向的值都不能改变
相同点:都可以对变量进行改变
不同点:
1)指针本质是一个变量,需要分配内存,引用是给一个对象起别名,不需要分配内存
2)引用在定义时必须初始化,而指针可以不用初始化
3)指针初始化可以为空,但是引用必须是一个已有对象的引用
4)指针和引用的自增运算结果不同。指针是指向下一个地址,而引用是引用的变量值+1
5)Sizeof不同,指针得到的是指针的大小,引用得到的是引用变量的大小。
定义:同一操作作用于不同的对象,产生不同的执行结果。C++多态意味着当调用虚成员函数时,会根据调用类型对象的实际类型执行不同的操作。
在日常生活中的多态例子:买票时,成人买票全价,如果是学生那么半价,如果是军人,就可以优先买票。不同的人买票会有不同的实现方法,这就是多态。
C++的多态必须满足两个条件:
1 必须通过基类的指针或者引用调用虚函数
2 被调用的函数是虚函数,且必须完成对基类虚函数的重写
实现:通过虚函数实现,用virtual声明的成员函数就是虚函数,允许子类重写。声明基类的指针或者引用指向不同的子类对象,调用相应的虚函数,可以根据指针或引用指向的子类的不同从而执行不同的操作。
Overload(重载):函数名相同,参数类型或顺序不同的函数构成重载。
Override(重写):派生类覆盖基类用virtual声明的成员函数。
Overwrite(隐藏):派生类的函数屏蔽了与其同名的基类函数。派生类的函数与基类函数同名,但是参数不同,隐藏基类函数。如果参数相同,但是基类没有virtual关键字,基类函数将被隐藏。
1)带有虚函数的类,编译器会为其分配一个虚函数表(用数组实现),里面记录了虚函数的地址,当此类被继承时,若子类重写了虚函数,则在子类的虚函数表中覆盖父类的虚函数地址,否则继承父类的虚函数地址。
2)实例化后,对象有一个虚函数指针,虚函数指针指向虚函数表,在程序运行时,通过虚函数指针找到虚函数表中对应的函数地址,调用虚函数
vector:向量,连续存储,可随机访问。
deque:双向队列,连续存储,随机访问。
list:链表,内存不连续,不支持随机访问。
stack:栈,不可随机访问,只允许再开头增加/删除元素。
queue:单向队列,尾部增加,开头删除。不支持随机访问
set:集合,采用红黑树实现,可随机访问。查找、插入、删除时间复杂度为O(logn)。
map:图,采用红黑树实现,可随机访问。查找、插入、删除时间复杂度为O(logn)。
hash_set:哈希表,随机访问。查找、插入、删除时间复杂读为O(1)。
C语言是面向过程的,C++是C的超集,继承并扩展了C语言,是面向对象的
1)struct的成员默认是public属性的,class的成员默认是private属性的
2)struct继承默认是public属性的,class继承默认是private属性的
3)“class”这个关键字还用于定义模板参数,就像“typename”。但关键字“struct”不用于定义模板参数
extern "C" 的主要作用就是为了能够正确实现C++代码调用其它C语言代码。加上extern "C"后,会提示编译器这部分代码按C语言(而不是C++)的方式进行编译。
重载:位于同一个类中,函数名字相同,形参列表不同
重写:被重写的函数必须是virtual,一般用于子类在继承父类是,重写父类中的方法,函数名称和参数列表都相同
多态:要有继承,要有虚函数重写,父类指针或引用指向子类对象。
多态:就是多种形态,C++的多态分为静态多态和动态多态。静态多态就是重载,因为在编译器决议确定,所以成为静态多态。动态多态即运行时多态是通过继承重写基类的虚函数实现的多态,因为在运行时决议确定,所以称为动态多态,也叫运行时多态。
单继承:一个子类只有一个直接父类时称这个继承关系为单继承。将基类虚表中的内容拷贝一份到子类虚表中,如果派生类重写了基类某个虚函数,就用派生类自己的虚函数替换掉原先基类虚函数的入口地址。
多继承:一个子类有两个或以上直接父类时称这个继承关系为多继承。多重继承会有多个虚函数表,几重继承就会有几个虚函数表。这些表按照派生的顺序一次排列,如果子类改写了父类的虚函数,那么就会用子类自己的虚函数覆盖虚函数表相应的位置,如果子类有新的虚函数,那么就添加到第一个虚函数表的末尾。
虚继承:C++使用虚拟继承,解决从不同途径继承来的同名的数据成员在内存中有不同的拷贝造成数据不一致问题,将共同基类设置为虚基类。这时从不同的路径继承过来的同名数据成员在内存中就只有一个拷贝,同一个函数名也只有一个映射。
虚函数是有代价的,由于每个虚函数的对象都要维护一个虚函数表,因此在使用虚函数的时候会产生一定的系统开销,这是没有必要的。另外,虚函数的调用相对于普通函数要更慢一些,因为每次都要查找虚函数表,有一定的时间开销。
非虚继承时,显然D会继承两次A ,内部就会存储两份A的数据,浪费空间,而且还会有二义性,D调用A的方法时,由于有两个A,究竟调用哪个A的方法呢,编译器也不知道,就会报错,所以有了虚继承,解决了空间浪费以及二义性问题。在虚继承下,只有一个共享的基类对象被继承,而无论该基类在派生层次中出现多少次。共享的基类子对象被称为虚基类,在虚继承下,基类对象的复制以及由此引起的二义性被消除了。
1.构造函数不可以定义为虚函数:虚函数的调用需要通过虚函数指针指向虚函数表,虽然虚函数表在编译时就有,但虚函数指针在创建对象之后才有,因此无法调用虚构造函数
2.析构函数一般定义为虚函数:在动态多态时,子类继承父类,子类重写父类的虚析构函数,父类指针或引用指向子类,这样就可以通过父类指针调用子类的析构函数释放子类中开辟的内存。
这是因为对象有两种构造方式,栈构造和堆构造,所以在对应的实现上,对象也有两种析构方法,其中堆上的对象和栈上的对象的析构不同之处在于,栈内存的析构不需要执行delete函数,会自动被回收。
因为如果不设置成虚函数,析构的过程中只会调用到基类的析构函数而不会调用子类的析构函数,可能会产生内存泄漏。
volatile关键字告诉编译器其修饰的变量是易变的,它会确保修饰的变量每次读操作都从内存里读取,每次写操作都将值写到内存里。volatile关键字就是给编译器做个提示,告诉编译器不要对修饰的变量做过度的优化,提示编译器该变量的值可能会以其它形式被改变。
volatile用于读写操作不可以被优化掉的内存,用于特种内存中。
(volatile 意思是易变的,是一种类型修饰符,在C/C++中用来阻止编译器因误认某段代码无法被代码本身所改变,而造成的过度优化。编译器每次读取 volatile 定义的变量时,都从内存地址处重新取值。
这里就有点疑问了,难道编译器取变量的值不是从内存处取吗?
并不全是,编译器有时候会从寄存器处取变量的值,而不是每次都从内存中取。因为编译器认为变量并没有变化,所以认为寄存器里的值是最新的,另外,通常来说,访问寄存器比访问内存要快很多,编译器通常为了效率,可能会读取寄存器中的变量。但是,变量在内存中的值可能会被其它元素修改,比如:硬件或其它线程等。)
C++中有四个强制类型转换的关键字
1.static_cast:用于数据类型的强制转换,强制将一种数据类型转换为另一种数据类型。
用法:static_cast <类型说明符> (变量或表达式)
int a = 10;
int b = 3;
double result = static_cast(a) / static_cast(b);
它主要有如下几种用法:
(1)用于类层次结构中基类和派生类之间指针或引用的转换
进行上行转换(把派生类的指针或引用转换成基类表示)是安全的
进行下行转换(把基类的指针或引用转换为派生类表示),由于没有动态类型检查,所以是不安全的
(2)用于基本数据类型之间的转换,如把int转换成char。这种转换的安全也要开发人员来保证
(3)把空指针转换成目标类型的空指针
(4)把任何类型的表达式转换为void类型
注意:static_cast不能转换掉expression的const、volitale或者__unaligned属性。
static_cast:可以实现C++中内置基本数据类型之间的相互转换。
2.const_cast:用于去除指向常量对象的指针或引用的常量性,其去除常量性的对象必须为指针或引用
用法:const_cast
该运算符用来修改类型的const或volatile属性。除了const 或volatile修饰之外, type_id和expression的类型是一样的。
常量指针被转化成非常量指针,并且仍然指向原来的对象;
常量引用被转换成非常量引用,并且仍然指向原来的对象;常量对象被转换成非常量对象。
const int a = 10;
const int * p = &a;
int *q;
q = const_cast(p);
*q = 20; //fine
3.reinterpret_cast:reinterpret_cast主要有三种强制转换用途:改变指针或引用的类型、将指针或引用转换为一个足够长度的整形、将整型转换为指针或引用类型。
用法:reinterpret_cast
type-id必须是一个指针、引用、算术类型、函数指针或者成员指针。
它可以把一个指针转换成一个整数,也可以把一个整数转换成一个指针(先把一个指针转换成一个整数,在把该整数转换成原类型的指针,还可以得到原先的指针值)。
4.dynamic_cast:
用法:dynamic_cast
(1)其他三种都是编译时完成的,dynamic_cast是运行时处理的,运行时要进行类型检查。
(2)不能用于内置的基本数据类型的强制转换。
(3)dynamic_cast转换如果成功的话返回的是指向类的指针或引用,转换失败的话则会返回NULL。
(4)使用dynamic_cast进行转换的,基类中一定要有虚函数,否则编译不通过。
dynamic_cast用于类继承层次间的指针或引用转换。主要还是用于执行“安全的向下转型(safe downcasting)”,
也即是基类对象的指针或引用转换为同一继承层次的其他指针或引用。
至于“先上转型”(即派生类指针或引用类型转换为其基类类型),本身就是安全的,尽管可以使用dynamic_cast进行转换,但这是没必要的, 普通的转换已经可以达到目的,毕竟使用dynamic_cast是需要开销的。
对于那些函数,它们只使用传递过来的值,而不对值进行修改
1)如果数据对象很小,如内置数据类型或小型结构,使用按值传递
2)如果数据对象是数组,则使用指向const的指针
3)如果数据对象是较大的结构,则使用const指针或者const引用,以提高程序的效率
4)如果数据对象是类对象,则使用const引用
对于那些函数,它们需要修改传递过来的值
1)如果数据对象很小,如内置数据类型或小型结构,则使用指针
2)如果数据对象是数组,则只能使用指针
3)如果数据对象是较大的结构,则使用指针或者引用,以提高程序的效率
4)如果数据对象是类对象,则使用引用
内存泄漏是指程序向系统申请分配内存使用(new),用完却没有归还(delete),结果申请的那块内存程序不能使用,而系统也无法再将它分配给需要的程序。
如何避免:
1.将基类的析构函数设为虚函数
2.new和delete配对使用
3.使用智能指针
C++面向对象的三大特性是封装、继承、多态。
C++面向对象编程就是把一切事物都变成一个个对象,用属性和方法来描述对象的信息,比如定义一个猫对象,猫的眼睛、毛发、嘴巴就可以定义为猫对象的属性,猫的叫声和走路就可以定义为猫对象的方法。
代码区:存放函数体的二进制代码,由操作系统进行管理
全局区(静态区):存放全局变量,静态变量和常量
栈区:存放函数参数、局部变量等
堆区:由程序员进行手动分配和释放,若程序员不释放,在程序结束时有操作系统进行回收。
纯虚函数是只有声明没有实现的虚函数,包含纯虚函数的类是抽象类,不能被实例化,只有实现了这个纯虚函数的子类才能生成对象。
浅拷贝是对对象指针的复制,原对象和副本指向的是相同资源
深拷贝是开辟一片新的内存空间,将原对象的资源复制到新的空间,返回新空间的地址
深拷贝可以避免重复释放和写冲突。
悬挂指针:当指针所指向的对象被释放后,指针的指向没有改变,还是指向被回收的内存地址
野指针:未初始化的指针
空指针:使用null或者nullptr初始化,不指向任何对象
Void指针:一种特殊类型的指针,可以存放任意对象的地址,任意类型的指针可以指向void指针,反之不行,void指针需要强制类型转换才能赋值给其他指针
1.new和delete是c++的关键字/运算符,malloc和free是c++/c语言的标准函数
2.malloc需要显示地指定分配的内存大小,new不需要
3.new操作符从自由存储区上为对象动态分配内存空间,而malloc函数从堆上分配内存。注:凡是通过new操作符进行内存申请,该内存即为自由存储区
4.new操作符内存分配成功时,返回对象类型,无需进行类型转换,故new是符合类型安全的操作符,malloc返回void*,需要通过强制类型转换将void*指针转换成我们需要的类型
5.new操作符内存分配失败时,抛出bad_alloc异常,malloc内存分配失败时返回NULL
6.new操作符有构造和析构函数,在开辟空间的同时,会调用自定义对象的构造函数来完成初始化,malloc只会开辟空间
7.new相对malloc效率要低,因为new的底层封装了malloc
可以,但是一般不这么用。malloc和free是C语言中的函数,C++为了兼容C语言保留下来这一对函数。简单来说,new可以理解为,先执行malloc来申请内存,后调用构造函数来初始化对象,delete是先执行析构函数,后使用free来释放内存。若先new再使用free来释放空间的话,可能会出现一些错误。而先使用malloc,再使用delete的话没有问题。
因为不能保证程序员使用free时传入的参数是和malloc一致的,从而导致内存泄漏等问题。现在free的解决方式是让free函数自己确定要释放多少内存,可以使用的方式是在申请内存时多申请一些空间来存储内存大小,在free时再获取这个大小进行释放。
1.不一定,当类型为int、float等内置类型时,可以不配对使用,但是建议还是配对使用。
2.new[]为一个数组申请内存时,编译器还会悄悄地在内存中保存一个整数,用来表示数组中元素的个数。因为在delete一块内存时,我们不仅要知道指针指向多大的内存,更重要的是要知道指针指向的数组中对象的个数。因为只有知道了对象数量才能一一调用它们的析构函数,完成对数组中所有对象的清理。如果使用的是delete,则编译器只会将指针所指的对象当作单个对象来处理。所以对于数组,需要使用delete[]来处理,符号[]会告诉编译器在delete这块内存时,先去获取保存的那个元素数量值,然后再进行一一清理。
在析构指向子类的父类指针时,根据虚函数表找到子类的析构函数,从而正确释放子类对象的资源
1)一个对象以值传递的方式传入函数体
2)一个对象以值传递的方式从函数返回
3)一个对象需要另一个对象进行初始化
当一个对象需要以值方式传递时,编译器会生成代码调用它的拷贝构造函数以生成一个复本。如果类A的拷贝构造函数是以值方式传递一个类A对象作为参数的话,当需要调用类A的拷贝构造函数时,需要以值方式传进一个A的对象作为实参; 而以值方式传递需要调用类A的拷贝构造函数;结果就是调用类A的拷贝构造函数导致又一次调用类A的拷贝构造函数,这就是一个无限递归。
指针传递的是变量“地址”,引用传的是变量“别名”。区别就是指针传递的时候是新声明了一个指针(指向相同地址)然后传进来,而引用则是将变量本身传进来(只不过换了个名字)。都可以对原值进行访问和修改,无需创建副本。
成员初始化列表就是在类或者构造函数中,在参数列表后以冒号为开头,逗号为分隔的一系列初始化字段
快:因为使用成员初始化列表进行初始化的话,会直接调用传入参数的拷贝构造函数进行初始化,省去了一次执行传入参数的默认构造函数的过程。
静态链接:在链接阶段,将源文件中用到的库函数与汇编生成的目标文件合并生成可执行文件;
优点:方便移植,执行速度快
缺点:占内存,每次更新都要重新链接
动态链接:把调用的函数所在的文件模块和调用函数所在文件的位置等信息链接进目标程序,程序在运行时再从文件模块中寻找相应的代码。
优点:应用灵活,不占内存
缺点:速度不如静态链接快
1)带有虚函数的类,编译器会为其分配一个虚函数表(用数组实现),里面记录了虚函数的地址,当此类被继承时,若子类重写了虚函数,则在子类的虚函数表中覆盖父类的虚函数地址,否则继承父类的虚函数地址。
2)实例化后,对象有一个虚函数指针,虚函数指针指向虚函数表,在程序运行时,通过虚函数指针找到虚函数表中对应的函数地址,调用虚函数
指针函数:就是一个返回指针的函数,其本质是一个函数,而该函数的返回值是一个指针。
int *fun(int x,int y);
函数指针:其本质是一个指针变量,该指针指向这个函数。总结来说,函数指针就是指向函数的指针。
int (*fun)(int x,int y);
为什么要进行内存对齐?比如结构体的成员可以有不同的数据类型,所占的大小也不一样。同时,由于CPU读取数据是按块读取的,内存对齐可以使得CPU一次就可以将所需的数据读进来
对齐规则:第一个数据成员放在偏移量为0的地方,以后每个数据成员的起始位置都要从自身大小的整数倍开始存储
内存对齐的好处:1.加快内存读取效率2.便于在不同平台之间移植
内存对齐的缺点:空间浪费
auto:可以让编译器在编译时就推导出变量的类型
decltype:用于推导表达式类型
int func() { return 0; };
decltype(func()) i; // i是int
int x = 0;
decltype(x) y; // y是int
decltype(x + y) z; // z是int
左值:在内存中有确定存储地址、有变量名、表达式结束依然存在的值。
左值引用:绑定到左值的引用,通过&来获得左值引用。
右值:在内存中没有确定存储位置、没有变量名,表达式结束就会销毁的值。
右值引用:绑定到右值的引用,通过&&来获得右值引用。
int a1 = 10; // 非常量左值
const int a2 = 10; // 常量左值
int& b1 = a1; // 非常量左值引用
const int& b2 = a2; // 常量左值引用
int&& c1 = 10; // 非常量右值引用
const int&& c2 = 10; // 常量右值引用,10是非常量右值
移动语义:可以理解为转移所有权,拷贝是对于别人的资源,自己重新分配一块内存存储复制过来的资源,而对于移动语义,类似于转让或者资源窃取的意思,对于那块资源,转为自己所拥有,别人不再拥有也不会再使用,通过C++11新增的移动语义可以省去很多拷贝负担,如何利用移动语义,主要通过移动构造函数。
完美转发:指可以写一个接受任意实参的函数模板,并转发到其它函数,目标函数会收到与转发函数完全相同的实参。转发函数实参是左值那目标函数实参也是左值,转发函数实参是右值那目标函数也是右值。
std::function是C++11标准库中提供的一种可调用对象的通用类型,它可以存储任意可调用对象,如函数指针,函数对象,成员函数指针和lambda表达式。std::function类模板是一个类似于函数指针的类型,但它是可以处理任意可调用对象的,并且可以检查调用对象是否为空。
基本语法:
std::function
int func(int x, int y) { return x + y; }
std::function f = func;
class A {
public:
int mem_func(int x) { return x * x; }
};
std::function f2 = &A::mem_func;
std::function对象可以像普通函数一样调用,并且可以使用bool类型的运算符来检查调用对象是否为空。
std::function f;
if (f)
std::cout << f(1, 2) << std::endl;
else
std::cout << "f is empty" << std::endl;
std::bind的头文件是
std::bind主要有以下两个作用:
1.std::bind绑定普通函数
double callableFunc (double x, double y) {return x/y;}
auto NewCallable = std::bind (callableFunc, std::placeholders::_1,2);
std::cout << NewCallable (10) << '\n';
1.bind的第一个参数是函数名,普通函数做实参时,会隐式转换成函数指针。因此std::bind(callableFunc,_1,2)等价于std::bind (&callableFunc,_1,2);
2._1表示占位符,位于
3.第一个参数被占位符占用,表示这个参数以调用时传入的参数为准,在这里调用NewCallable时,给它传入了10,其实就想到于调用callableFunc(10,2);
2.std::bind绑定一个成员函数
class Base
{
public:
void display_sum(int a1, int a2)
{
std::cout << a1 + a2 << '\n';
}
int m_data = 30;
};
int main()
{
Base base;
auto newiFunc = std::bind(&Base::display_sum, &base, 100, std::placeholders::_1);
f(20); // should out put 120.
}
1.bind绑定类成员函数时,第一个参数表示对象的成员函数的指针,第二个参数表示对象的地址。
2.必须显式地指定&Base::diplay_sum,因为编译器不会将对象的成员函数隐式转换成函数指针,所以必须在Base::display_sum前添加&;
3.使用对象成员函数的指针时,必须要知道该指针属于哪个对象,因此第二个参数为对象的地址 &base;
3.绑定一个引用参数
枚举作用域是指枚举类型成员名字的作用域,起自其声明之处,终止枚举定义结束之处。enum与class enum区别在于是否限定其作用域。C语言规定,枚举类型(enum)的成员的可见范围被提升至该枚举类型所在的作用域内。这被认为有可能污染了外部的作用域,为此,C++11引入了枚举类(enum class)解决此问题。
#enum
enum Sex
{
Girl,
Boy
};
// 错误,编译器提示 Girl,Boy重定义
enum Student
{
Girl,
Boy
};
#enum class
enum class Sex
{
Girl,
Boy
};
enum class Student
{
Girl,
Boy
};
int main(int argc, char *argv[])
{
Sex a = Sex::Gril;
Student b = Student::Gril;
//两者处于不同作用域下,不会重定义
}
枚举定义将被限制在枚举作用域内,并且不能隐式转换为整数类型,但是可以显式转化为整数类型,
enum class Sex
{
Girl,
Boy
};
int main(int argc, char *argv[])
{
Sex a=Sex::Gril;
int d1 =a; // 错误,无法从“Girl”隐式转换为“int”。
int d2 = int(a); // 正确,显示将enum class转换为整数
return 0;
}
array是C++11中新提出来的容器类型,与内置数组相比,array是一种更容易使用,更加安全的数组类型,可以用来替代内置数组。作为数组的升级版,继承了数组最基本的特性,也融入了很多容器操作
数组初始化不能直接使用拷贝和赋值!!!数组的传递只能是遍历的形式来拷贝
和数组不同的是,array可以使用拷贝和赋值的形式进行初始化
array
array
reserve()函数:会有可能将容量变大
(1)如果n大于容器现有的容量(capacity),比如你容器原来是100的容量,我现在指定n=200,那么就需要在自由内存区为整个容器重新分配一块新的更大的连续空间【因为vector是顺序容器,所以存储空间是连续的,如果之前的存储空间不够了,必须这样做】,然后将容器内所有的有效元素从旧位置全部复制到新位置,这个过程是调用拷贝构造函数,然后释放旧位置的所有存储空间,并调整容器的元素位置指示器。所以reserve的时候如果n比原来的大,结果只是让容器的冗余容量(即没有分配元素的存储区)变大,容器的实际大小,即元素个数并没有改变。
(2)如果n小于容器原来的容量,那么这个函数什么作用也没有了
resize()函数:会改变size的大小
(1)如果n大于容器当前的大小(即容器的size,这里和capacity无关),则在容器的末尾插入n-size()个初始值为c的元素;如果没有指定初始值,那就元素类型的默认构造函数来初始化。
(2)如果n小于容器当前的大小,则删除末尾的size()-n个元素,这样就导致容器的大小变了,size 变小了。但是这种类型的容器在删除一个元素的时候并不会释放元素本身的内存空间【这也是为了保留这块空间以避免将来要插入新元素的时候又要进行存储空间重分配】,所以容器的容量即capacity其实是没有改变的。
(3)n等于容器当前的大小,则什么也不做。
reserve和resize的共同点就是:都不缩减容器本身的容量。即对内存空间并没有影响
clear实现:clear只是将vector的size置0,并不保证capacity为0,因此clear并不能释放vector已经申请的内存。
=,assign() // 赋值
swap() // 交换
+=,append(),push_back() // 尾部添加字符
insert() // 插入字符
erase() // 删除字符
clear() // 清空字符
replace() // 替换字符
substr() // 截取子字符串
C++ Qt常用面试题整理(不定时更新)_qt面试题-CSDN博客