C++ -学习笔记 --《c++ primer plus》
C++
1. C++基础知识
-
main()
函数由启动代码调用,它是由编译器添加到程序中的。 -
C头文件被转换成C++头文件,去掉拓展名h,在开头加上c。代码加入了命名空间std用法。eg.math.h --> cmath
-
使用
endl
可以刷新输出,而\n
不能保证。使用dec、oct、hex
来输出不同进制数据。 -
C++提供两种发送消息的方式,一种是使用类方法(函数调用),另一种是重定义运算符。
-
函数原型只描述接口,即描述的是发送给函数的信息和返回的信息;函数定义包含了函数的代码。
-
using
编译指令、using
声明指令,::
为作用域解析运算符,尽量使用声明。 -
short
至少16位;int
至少与short
一样长;long
至少32位,且至少与int
一样长;long long
至少64位,且至少与long
一样长。 -
可以使用大括号初始化器来初始化常规变量和类变量,等号是可选项。
e.g. int a{1}; ==> int a=1;
C++11 -
强制类型转换方式
(typename) value
来自C语言。typename(value)
纯粹的C++格式。- 采用强制类型转换运算符,
static_cast<typename> (value)
。 reinterpret_cast
是进行高风险转换,比如整型转换成指针,但不能将函数指针转换成数据指针。const_cast
可以改变值为const
或volatile
,一般在需要删除const
标签时使用。但原来的值并不能真正被修改,虽然都是指向相同的地址。dynamic_cast
-
c++包含两种类型:基本类型和复合类型。基本类型包含整数和浮点数;复合类型包含数组、字符串、指针、结构、共用体、枚举、类等。
-
cin
读取一个单词,会保留换行符到缓冲区,输入到字符串中会自己补上'\0'
,若输入格式错误,返回0;cin.getline()
读取整行并删除换行符,cin.get()
读取整行并保留换行符到输入序列。 -
cin.fail()
可用来判断是否到达文件尾EOF
,格式错误也会返回true。cin.get(ch)
,将读到的值给ch
,若是EOF
,则返回false
;ch=cin.get()
,返回int
类型编码,若是EOF
,则也返回EOF
给ch
。 -
getline(cin,str)
将读取一行字符进string
对象中。string
对象也可以使用数组表示法来访问字符串中的字符。 -
字符串字面量(字符串常量)
- char类型,8位
wchar_t
类型,L"xxxxx",16位~32位 经测试ubuntu为32位char16_t
类型,u"xxxxx",16位 C++11char32_t
类型,U"xxxxx",32位 C++11- raw(原始)字符串,R"(ssdasad)",可与其他类型字符串结合使用,RU、Ru等等 C++11
- utf-8格式,u8"xxx",根据编码的数字值,可能存储为1-4个字节。
-
C++声明结构体时可以省略struct,C不可以。支持对象作为结构的成员变量(如string等(a))。会按照最宽基本类型成员大小的整数倍进行字节对齐。
-
C++ 中保留了C语言的
struct
关键字,并且加以扩充。在C语言中,struct
只能包含成员变量,不能包含成员函数。而在C++中,struct
类似于class
,既可以包含成员变量,又可以包含成员函数。 -
C++中的
struct
和class
基本是通用的,唯有几个细节不同- 使用
class
时,类中的成员默认都是private
属性的;而使用struct
时,结构体中的成员默认都是public
属性的。 class
继承默认是private
继承,而struct
继承默认是public
继承。- struct 一般用于描述一个数据结构集合,而 class 是对一个对象数据的封装。
class
可以用于定义模板参数的类型,而struct
不能。
- 使用
-
枚举可以赋值负数,后面的仍然依次加一。
-
计算机在存储数据时必须跟踪三种基本属性:地址、值、类型。
-
与
malloc
相似,C++用new
用来申请内存,释放内存用delete
。delete
只会释放指针指向的内存,不会删除指针本身,即指针本身可以重复使用去指向另一个新分配的内存。只能用于释放new
申请的内存,不要用于释放声明变量所获得的内存,也不要重复释放相同内存块。 -
vector
模板类,是一种动态数组。array
(C++11)模板类,与数组相同,是静态内存分配(放在栈中)。 -
++ch
输出字符,ch+1
输出数字。 -
对于内置模式,前缀模式(如++a)与后缀模式没有什么差别。但对于自定义模式,因为要先复制一个副本将其加或减一,再返回结果,所以前缀模式的效率更高。
-
用引号括起的字符串常量是其地址。
-
文件输出/读取主要步骤:
- 包含头文件
fstream
- 创建一个
ofstream或ifstream
。 - 将该对象与文件关联起来。
- 像使用
cout或cin
那样使用它。
- 包含头文件
-
函数名是该函数的地址,加上小括号是返回值。
-
内联函数是将相应的函数代码替换函数调用,比常规函数梢快,但需要占用更多内存。主要节省了函数调用机制的时间。需要在函数声明、定义前加
inline
。一般直接在声明出直接定义。不可以递归,否则编译器会认为常规函数。 -
ifstream.good()
没有发生任何事情的时候返回true
,ifstream.eof()
遇到文件尾返回true
,ifstream.fail()
类型不匹配或者文件尾返回true
,ifstream.bad()
文件受损或者硬件故障返回true
。 -
引用变量,是已定义变量的一个别名,用&定义。指向相同的值和内存单元。必须声明时初始化。接近
const
指针。 -
函数使用引用参数(必须是变量,不能是表达式,加上
const
可以生成临时变量再引用,此时可以转换类型),允许被调函数访问主调函数的变量。当然用指针也可以实现。节约时间和内存。 -
函数可以返回一个
const
值,防止返回值成为左值。 -
函数模板使用泛型来定义函数,其中的泛型可用具体的类型替换。通过将类型作为参数传递给模板,可使编译器生成该类型的模板。可用
typename
、class
来声明参数类型。 -
具体化函数优先于常规模板函数,具体化包括:隐式实例化、显式实例化、显式具体化。是处理具体类型的函数。
template<>
为显式具体化、template
为显式实例化。 -
decltype
用在函数模板中,提供参数的类型。auto
和decltype
结合使用,定义模板函数。C++11 -
定位
new
运算符,可以指定要使用的位置。需要包含new
头文件。int *p = new (buffer) int;
2.存储持续性、作用域和链接性
- 存储持续性(c中称存储类别),c++使用四种同的方案来存储数据,区别在于数据保留在内存中的时间
- 自动存储持续性(期),执行函数、代码块时被创建,执行完毕就释放。
- 静态存储持续性(期),在整个程序运行过程中都存在。
- 线程存储持续性(期)(C++11),与对应线程生命周期相同。
- 动态存储持续性(期),用
new
等关键字自己分配的,直到自己delete
或者程序结束。
- 作用域描述了名称在文件的多大范围内可见。
- 链接描述了名称如何在不同单元之间共享。
mutable
用来指出,即使结构(或类)变量为const
,某个成员也可以被修改。
3. 对象和类
-
对象实例化方法
copy- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
//假设有构造方法 Person(string name) //小括号是调用构造方法 Person p = Person("rui"); // 主要形式 Person p("rui"); // 短形式 Person *ptr = new Person("rui"); // 动态对象,需要自己释放内存 //若支持C++11,可以使用大括号初始化 Person p = {"rui"}; Person p{"rui"}; person *ptr = new Person{"rui"};
-
类型名加括号生成匿名对象,生命到下一行就结束,如
Person();
。 -
类对象默认访问控制为private,所以可以不写private。
-
定义成员函数时,要使用作用域解析运算符来标识函数所属的类。
-
定义位于类声明中的函数都将自动成为内联函数,声明外的成员函数加上
inline
也可以成为内联函数。 -
每个对象都有自己的存储空间,但是同一个类的所有对象共享同一组方法,即每个方法只有一个副本。
-
要创建类对象,可以声明类变量(显式构造、隐式构造),也可以使用new为对象分配存储空间。没有提供构造函数时,编译器会提供默认构造函数,若提供了非默认构造函数,则使用自定义构造函数。可以用大括号初始化C++11。
-
对象的的生命周期与该对象的存储持续性相同,对象释放时将调用析构函数。
-
可以在内部方法后面加上
const
关键字,表示类方法不会修改调用对象,称为const成员函数。不是类成员函数不允许在后面加const
。- const对象只能访问const成员函数
-
this
指针指向调用成员函数的对象(被作为隐藏参数传递给方法),注意this是指针,*this
是该对象,与java不同。每个成员函数都有一个this
指针。 -
在c++类中放一个匿名
union
,可以直接使用其中元素的名字来访问该元素。copy- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
class Person{ private: union{ int i; double m; } ... } int main() { Person p; //匿名联合可以直接访问 p.i=10; ... //匿名联合可以直接访问 p.m=11.29; ... return 0; }
-
可以使用枚举或者关键字
static const
来创建一个所有对象共享的常量。作用域内枚举(enum class egg{ small, large}
)不能隐式的转换成整型。- 当变量声明为static时,空间将在程序的生命周期内分配
- 静态变量不能使用构造函数初始化
-
可以使用
operatorop()
来重载运算符,op是运算符。如operator*()
,*
将被重载。掉用方法时可以使用运算符表示法,符号左侧是调用对象,右侧是参数。也可以使用常规的形式调用方法。 -
重载运算符的限制:
- 重载的运算符必须至少有一个操作数是用户定义的类型。
- 使用运算符时不能违反原来的句法规则,不能修改运算符的优先级。
- 不能创建新的运算符。
- 不能重载规定外的运算符。通过成员函数或非成员函数进行重载,非成员函数比成员函数多一个参数,也不能同时定义。
-
友元函数,通过让函数(函数原型添加
friend
关键字)成为类的友元,可以赋予该函数与类成员函数相同的访问权限,如访问私有变量。 -
转换函数
- 将单个值转换成类类型。 当构造参数只有一个,或者其他参数拥有默认值时,可以直接用=进行隐式的转换,当在构造函数声明前面加上
explicit
时将屏蔽这种隐式转换,此时值能进行显式转换。也就是利用构造函数可以将某种类型转换成类类型。 - 将类类型转换成其他类型。与构造函数的转换相反,是将类对象转换成所需要的类型。实现类方法
operator typename()
即可自动调用。C++11中可以加上explicit
关键字,将不会隐式转换,只能显式转换(注意C++98中不允许explicit
加在函数前面)。explicit
只能处于类函数原型中,定义时不加。- 转换函数必须是类方法。
- 转换方法不能声明返回类型,但是可以返回所需的值。
- 转换函数不能有参数。
- 将单个值转换成类类型。 当构造参数只有一个,或者其他参数拥有默认值时,可以直接用=进行隐式的转换,当在构造函数声明前面加上
-
静态类成员的特点(
static
修饰),所有对象都将只创建一个静态类变量副本。不能在类声明中初始化静态成员变量。当静态数据成员为const
整数类型或者枚举型时可以在声明时初始化。static
静态成员变量不能在类声明处初始化(不是和类的具体对象相关的,没有具体对象,也能调用类的静态成员函数和成员变量),const
成员变量也不能在声明中处初始化(只在某个对象生存期内是常量,而对于整个类而言却是可变的,且只能在类的构造函数的初始化列表中进行)。static const
可以在声明处初始化,类作用域,在整个类中都恒定的常量。- C++11允许类内初始化
-
C++会自动为类提供以下成员函数(如果没有定义),可以使用伪私有方法可以避免。
- 默认构造函数,没有任何参数,也不执行任何操作,若定义了构造函数则不会提供默认构造函数。
- 默认析构函数
- 默认复制构造函数,当新建一个对象并将其初始化成同类的现有对象时,会逐个赋值非静态成员(成员复制也叫浅复制),按值传递和返回对象时都会调用,静态数据不受影响。
- 赋值运算符,自动为类重载赋值运算符,也是浅复制,静态数据不受影响。将已有对象赋给另一对象时使用。初始化对象时不一定, e.g
Person p2=p1;
,会总是使用复制构造函数,但赋值构造函数不一定会有。 - 地址运算符
-
成员函数的默认值不能是非静态成员变量,因为不能保证解析函数时该成员变量存在。
-
注意不要将赋值与初始化搞错了,如果是创建新对象则使用初始化;如果是修改已有对象的值,则是赋值。
-
深度复制,如果类中包含了使用new初始化的指针成员,应当定义一个复制构造函数,以复制指向的数据,而不是指针。
-
浅复制或成员复制,只是复制指针值,不会复制指向的数据。
-
成员初始化列表语法
- 这种格式只能用于构造函数。
- 必须用这种格式来初始化非静态const数据成员。
- 必须用这种格式来初始化引用数据成员。
-
C++11提供新关键字
nullptr
来表示空指针。 -
静态的类成员函数,只能使用静态数据成员,不能通过对象进行调用,需要用类名加上作用域解析运算符来调用。如
String::HowMany()
。 -
需要显式的为定位new运算符创建的对象调用析构函数,且应以创建顺序相反的顺序进行删除,因为晚创建的对象可能依赖于早创建的对象,当所有对象都被销毁后才能释放存储这些对象的缓冲区。
-
给
const
常量数据和被声明为引用的类成员初始化时,需要在构造函数右边进行初始化,这会在创建对象前初始化,其他参数也可以。不过成员初始化列表语法只能用于构造函数。 -
C++11允许在类声明处初始化成员变量。
4. 类的继承
-
继承类(也称派生类,java称子类)不能直接访问基类(java称父类)的私有成员,而必须通过基类方法进行访问。也就是说派生类构造函数必须使用基类的构造函数。可以通过作用域解析运算符调用基类方法,若派生类没有重新定义该方法,则可以不用作用域解析运算符直接调用。
-
创建派生类对象时,会先创建基类的对象,可以使用成员初始化列表语法来完成。派生类对象过期时,先调用派生类析构函数,再调用基类析构函数。
-
基类指针或引用可指向派生类(反之不可以),但也只能调用基类的方法。
-
多态的实现,通常使用虚方法,程序会根据对象类型而不是引用或者指针的类型来选择方法版本。为基类声明一个需析构函数也是一种惯例。关键字
virtual
只用于函数原型,不用于定义中。 -
将派生类引用或者指针转换为基类引用或指针被称为向上强制转换(允许隐式转换)。将基类指针或者引用转换成派生类指针或引用被称为向下强制转换(需要显式转换)。
-
虚函数由编译器实现,会运用即可,不过一种实现方法是给每个对象隐藏添加了一个指向函数地址数组的指针,称为虚函数表。
-
虚函数注意事项:
- 构造函数不能是虚函数。
- 基类析构函数应当是虚函数,这样可以通过指向对象的基类指针或者引用来派生对象时,会先调用派生类的析构函数,然后调用基类的析构函数,而不仅仅调用基类的析构函数。
- 友元函数不能是虚函数,因为成员函数才能是虚函数。
- 派生类没有重新定义,将使用基类版本。
- 若重新定义基类的方法,则应该与原来的函数原型一致,但如果返回类型是基类引用或者指针,则可以修改为派生类引用或者指针。如果基类声明被重载了,应该重新定义所有基类版本,若不重新定义,则派生类将无法使用该函数。(由虚函数实现的按对象类型调用也会失效)
-
纯虚函数的结尾处加上
=0
,当类声明中存在纯虚函数时,则不能创建该类的对象。纯虚函数可以定义也可以不定义。 -
抽象类至少存在一个纯虚函数。
-
派生类虚函数最后可加
override
,保证派生类重写该函数和基函数已经定义该虚函数 -
当派生类没有使用动态分配
new
时,默认的构造、复制、赋值、析构函数是能正常处理的。 -
当派生类使用
new
时,将按下面操作自行定义:- 派生类析构函数自动调用基类析构函数,只需要处理自己的部分数据就行。
- 复制构造函数,需要派生类调用基类的复制构造函数。
- 赋值运算符,需要派生类显式调用基类赋值运算符。
-
友元函数不是成员函数,派生类不会继承,需要自己重新编写。
-
应该尽量按照按引用传递对象,节约资源,且在继承使用虚方法时,被定义为接受基类引用参数可以接受派生类对象。
-
尽量返回引用,但不可以返回临时对象的引用。由于返回引用的函数放在赋值语句的左侧,因此可以加上
const
来避免被修改。 -
参数使用
const
可以确保不修改参数,放在后面可以确保不修改它的对象。
5. C++中的代码重用
- 通常在单参数的构造函数前加上
explicit
关键字,防止隐式转换。 - 私有继承,基类的公有成员和保护成员都将成为派生类的私有成员。使用
private
进行继承(默认)。- 私有继承中,未进行显式类型转换的派生类引用或者指针,无法赋值给基类的引用或指针。即不能向上隐式转换。
- 保护继承,基类公有成员和保护成员都将成为派生类的保护成员。使用
protected
进行继承。- 派生类需要使用基类私有成员的话,则可以将基类该成员变为受保护成员。对于外部仍然是不能直接访问的。详情
- 对于私有继承、保护继承,可以使用
using
重新定义访问权限。 - 多重继承,在祖先相同时必须使用虚基类,并修改构造函数初始化列表的规则,通过优先规则解决名称歧义性。
- 使用基类指针来引用派生类对象时,若不是虚基类,则需要显式强制转换。
- 使用虚基类将使得从多个类(基类相同)派生出的对象只继承一个基类对象。继承时使用
virtual
关键字。 - 对于构造函数,使用虚基类时,禁止信息通过中间类自动传递给基类,基类会使用默认构造函数。如果不希望默认构造函数创建虚基类对象,需要显示的调用所需的基类构造函数。
- 如果从两个不同类继承了两个同名成员,应该使用
::
来区分。若使用虚基类,该方法不能叠加。 - 派生类中的名称优先于直接或者间接祖先类中的相同名称。
- 类模板,将类型名作为参数传递给接收方来建立类(或者函数,就是函数模板)。类开头加上
template<class Type>
,表示是模板类;加在成员函数上方,表示是函数模板。可以用作基类。 - 可以加入非类型参数如
template<class Type,int n>
,class
等价于typename
,表达式参数可以是整型、枚举、引用或指针,另外用作表达式参数n的值必须是常量表达式。使用表达式参数的方法的缺点主要是,每次改变n的值都会生成自己的模板,因此使用构造参数更通用。 - 与函数模板相同,有以下方式,统称为具体化,如果有多个模板可供选择,则选择具体化程度最高的。
- 隐式实例化
- 显式实例化,
template class Classname<T>;
- 显式具体化,
template <> class Classname<specialized-tyep-name>{...};
,当具体化模板与通用模板都与实例化请求匹配时,使用具体化版本。目的是对模板进行修改,使其行为不同。 - 部分具体化,该特性使得能够对类型参数设置各种限制,部分限制模板通用性。
- 模板可用作结构、类和模板类的成员。在类中加入模板类成员,若编译器支持可以采用类外定义,如下:
copy
- 1
- 2
- 3
//采用嵌套的方式
template<typename T>
template<typename V>
- 模板可以用作参数,模板可以包含本身就是模板的模板参数,如:
copy
- 1
- 2
- 3
template<template<typename T> class Thing>
class Crab{
}
其中template<typename T> class
是类型,Thing
是参数,类中可以使用Thing<int>
等作为模板参数的实例化。
- 模板类也可以有友元函数,包括非模板友元、约束模板友元、非约束模板友元。
- 可用
typdef xx newname
和using newname = xx
来设定别名。
6. 友元、异常和其他
- 相同class的各个object互为friend(友元)。
- 友元类,友元类的所有方法都可以访问原始类的私有成员和保护乘员。
friend class classname
可以放在公有、私有、保护位置,友元类的声明与位置无关。 - 友元成员函数,仅将特定类成员成为另一个类的友元。需要使用前向声明。
- 其他友元形式:相互互为友元类、一个函数是另外类的共同友元函数或者类。
- 嵌套类,在另一个类中声明的类。通常是帮助实现另一个类,并避免名称冲突。
void onoff(){state=(state==on)? Off : On;}
,若两种状态分别为true(1)和false(0),则可以用异或和赋值运算符简写,void onoff(){state ^= 1;}
- 异常代码块
try{...}catch( ){...}
,catch
可以捕获字符串,但通常是类类型(使用类引用)。 - 异常规范(C++98,C++11废弃但支持),它可能出现在函数原型和函数定义中(
void test(int a) throw()
),作用之一是告诉用户可能需要使用try块,另一作用是让编译器添加运行阶段检查代码,检查是否违反了异常规范。noexcept
指出函数不会发生异常(C++11)。 - 若发生未捕获异常,将默认情况下调用
terminate()
,terminate()
再调用abort()
;若发生异常规范中没有的异常(也没有std::bad_exception类型,否则会被改该类型替代
),则调用unexpected()
,它会去调用terminate()
。若不想程序终止,则应该重写上述方法。 exception
异常类在头文件exception
中,可以用它作为其他异常类的基类,what()
虚函数可以返回一个字符串。- 头文件
stdexcept
中定义了一些异常类,如logic_error
等。通过catch
不同的类名,可以实现分别处理异常;在具备继承关系的异常类中,可以实现一起处理。(要求catch块从派生类开始,一直到基类,再到上一层的基类的顺序排放) bad_alloc
异常可用于捕获new
分配时的异常,头文件new
中包含了该类的声明。由于之前的new
是返回空指针,所以C++提供了一个标志,让用户选择。int *pi = new (std::nothrow) int;
与int *pa = new (std::nowthrow) int[500];
。- 若使用栈区内存(自动管理),通过异常提前结束了函数,该内存也能被正确释放(栈解退);但若使用
new
、malloc
等关键字(堆内存管理),提前出现异常将导致内存泄漏(需要在catch中进行清理内存操作,或者使用智能指针模板)。 - RTTI(运行阶段类型识别)
dynamic_cast
运算符,可以回答“是否可以安全的将对象的地址赋值给特定类型的指针”这样的问题。dynamic_cast<type *>(pt)
,如果可以将对象地址转换成指向type
的地址,则返回该,否则返回空指针。也可以用于引用,当无法强制转换时,将引发bad_cast
异常,需要头文件typeinfo
。typeid
运算符,返回一个type_info
对象。type_info
类,在头文件typeinfo
中,其成员函数name()
将返回该类的名称。该类重载了==
和!=
,所以可以确定两个对象是否为相同类型。
7. string类和标准模板库
string
其实是basic_string
的typdefine
。string
构造函数,7个。输入方法2种。- 智能指针,将申请的内存地址放在一个指针对象中,并将释放该内存的方法,放在该指针类的析构函数中。有三个智能指针模板:
auto_ptr
(C++98提出,C++11已经废弃),unique_ptr
(C++11),shared_ptr
(C++11)。必须包含头文件memory
,位于名称空间std
中。不可以用于堆内存。
copy
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
//auto_ptr模板函数如下
template<class X> class auto_ptr{
public:
explicit auto_ptr(X *p=0) throw();
...
}
//使用
auto_ptr<double> pdu(new double);//获得一个指向double的auto_ptr
- 防止智能指针
delete
两次对象的方法- 定义赋值运算符,使之深复制。
- 建立所有权概念,对于特定的对象,只有一个智能指针能拥有它,
auto_ptr
和unique_ptr
采用这种方法。 - 创建更高智能指针,跟踪引用特定对象的智能指针数,到0时才
delete
,shared_ptr
采用这个方法。
auto_ptr
在将指针所有权给别人后,不可以再使用该指针。unique_ptr
会在编译阶段控制这种错误(auto_ptr
中的错误),但当unique_ptr
是一个临时右值时编译器不会报错,因为不会产生悬挂指针。使用std::move()
可以将一个unique_ptr
赋值给另一个unique_ptr
。当使用new[]
分配内存时,只能使用unique_ptr
.- 当程序要使用多个指针指向对象时,应该选择
shared_ptr
。 - STL(标准模板库),提供了一组表示容器、迭代器、函数对象和算法的模板。
- 迭代器是一个广义的指针,可以对其执行类似的如解除引用(
operator*()
)和递增(operator++()
)的对象。
copy
- 1
- 2
- 3
- 4
vector<double> scores;
vector<double>::iterator pd=scores.begin(); //迭代器类似于指针
*pd=22.3;
pd++;
- 泛型编程,关注的是算法,旨在编写独立于数据类型的代码。在C++中使用模板来实现按泛型定义的函数或类,而STL通过通用算法而更近了一步。
- 模板使得算法独立于存储的数据结构,而迭代器使算法独立于使用的容器类型。
- STL中有五种迭代器类型,分别为输入迭代器、输出迭代器、正向迭代器、双向迭代器和随机访问迭代器。
- C++将
operator++()
作为前缀版本,operator++(int)
作为后缀版本。- 前缀版本避免了不必要的工作,它把值加一后直接返回改变了运算对象。
- 后置版本需要将原始值存储下来以便返回这个未修改的值,所以如果不需要原来值,后置版本就是资源浪费。
- 只要提供适当的迭代器(可以是指针,也可以是对象)和超尾指示器,就可以将STL算法用于自己设计的数组形式。
- 函数对象也称函数符,是可以以函数方式与()结合使用的任意对象。
本文作者:oniisan
本文链接:https://www.cnblogs.com/oniisan/p/cppprimerplus.html
版权声明:本作品采用知识共享署名-非商业性使用-禁止演绎 2.5 中国大陆许可协议进行许可。
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步