**经过差不多一学期的学习,总结了一些C++的笔记现在分享一下,都是平时学习过程中遇到的困难,希望能对他人有所帮助吧,如果发现错误的地方可以留言,我尽可能修改**
1.C++相比C语言多出的东西
(1)定义了一个新的数据类型,布尔类型(Bool)
(2)新的变量赋值方式
C语言:int i = 2;
C++ : int i = 2 或者 int i(2);
(3)随用随定义
**2.**类和命名空间的区别
一般程序的开发都是有多个人共同开发的,为了防止不同模块的类和函数重名,所以采用命名空间来区分,这样就不怕同名的混乱了。
类就是面向对象所特有的,通过类来创建把自然界的事物封装起来来使用。类似于:命名空间是水果(集合),而类是苹果、香蕉等等(子集)。
3.函数参数默认值
有默认值的参数必须在参数列表的最右端
**4.**函数重载
在相同作用域之内,同一函数名定义的多个函数,参数个数和参数类型不同,叫做函数重载。
重载指的是函数具有的不同的参数列表,而函数名相同的函数。重载要求参数列表必须不同,比如参数的类型不同、参数的个数不同、参数的顺序不同。如果仅仅是函数的返回值不同是没办法重载的,因为重载要求参数列表必须不同。(发生在同一个类里)
覆盖是存在类中,子类重写从基类继承过来的函数。被重写的函数不能是static的。必须是virtual的。但是函数名、返回值、参数列表都必须和基类相同(发生在基类和子类)
重定义也叫做隐藏,子类重新定义父类中有相同名称的非虚函数 ( 参数列表可以不同 ) 。(发生在基类和子类)
**5.**拷贝构造函数
(1)如果没有自定义的拷贝构造函数,则系统自动生成一个默认的拷贝构造函数
(2)当采用直接初始化或复制初始化实例化对象是,系统自动调用拷贝构造函数
6. 构造函数总结
构造函数
**7.**析构函数
如果没有定义的析构函数则系统会自动生成
析构函数在对象销毁时自动调用
析构函数没有返回值,没有参数也不能重载
8. h文件里面不要去 定义函数,应该只进行声明,否则多个.cpp文件包含时,会发生重复定义错误
**9.**初始化列表在构造函数之前运行
10使用string类的时候,必须包含头文件string以及using namespace std。
11.this指针属于对象,初始化列表在构造函数之前执行,在对象还没有构造完成前,使用this指针,编译器无法识别。
12.类中成员函数的形参最好写成 new+成员变量 的形式,防止形参结合时的代码引起混淆
13.二维数组的初始化问题:
1.如果不做任何初始化,局部作用域的非静态数组中会存在垃圾数据,static数组中的数据默认初始化为0
2.如果只对部分元素初始化,剩下的未显式初始化的元素,将自动被初始化为0
14.友元:
对一些类外的函数,其他的类,给于授权,使之可以访问类的私有成员
15.标识符的可见性:
1.如果某个标识符在外层中声明,且在内层中没有同一个标识符的声明,则该标识符在内层可见
2.对于两个嵌套的声明,如果在内层作用域声明了与外层作用域同名的标识符,则外层作用域的标识符在内层不可见
16.静态生存期static:
1.这种生存期与程序的运行期相同
2.在文件作用域中声明的对象,具有这种生存期
3.在函数内部声明静态生存期对象,要冠以关键字static
用static来声明一个变量的作用有二:
(1)对于局部变量用static声明,则是为该变量分配的空间在整个程序的执行期内都始终存在。
(2)外部变量用static来声明,则该变量的作用只限于本文件模块。
17.静态数据成员static:
1.用关键字static声明
2.为该类的所有对象共享,静态数据成员具有静态生存期
3.必须单独地在类外定义和初始化,用(::) 来指明所属的类,且不能再类的构造函数的初始化列表中进行初始化
4.可以通过 类名::静态成员 来访问静态数据成员(初始化时用到),也可以通过 静态成员函数来访问
5.静态数据成员的值在const成员函数中可以被合法的改变。
6.静态成员函数不可以调用类的非静态成员,因为静态成员函数不含this指针,即静态成员函数不知道调用的是哪个对象的非静态成员
6.1博客园解释:
在c++中,静态数据成员可以被非静态成员函数调用吗?如果可以调用的话那为什么还要定义静态成员函数呢
静态数据成员可以被非静态成员函数访问。但静态成员函数只能访问静态数据成员。(存在争议)静态数据成员从属于某一个类,而不是某一个类的
对象。同理,静态成员函数也是一样。
追问
定义静态成员函数的作用有何在呢
回答
静态成员函数隶属于类,不用实例化对象,就可以直接调用静态成员函数来操作静态数据成员
提问者评价
太给力了,你的回答完美解决了我的问题!
7.引用优快云的理解:
我们知道C++类的静态成员变量是需要初始化的,但为什么要初始化呢。其实这句话“静态成员变量是需要初始化的”是有一定问题的,
应该说“静态成员变量需要定义”才是准确的,而不是初始化。两者的区别在于:初始化是赋一个初始值,而定义是分配内存。
静态成员变量在类中仅仅是声明,没有定义,所以要在类的外面定义,实际上是给静态成员变量分配内存。
8.正确的做法是将静态数据成员的初始化放在对应的.cpp文件中。
18.类的友元friend:
友元是C++提供的一种破坏数据封装和数据隐藏的机制。
通过将一个模块声明为另一个模块的友元,一个模块能够引用到另一个模块中本是被隐藏的信息。
可以使用友元函数和友元类。
为了确保数据的完整性,及数据封装与隐藏的原则,建议尽量不使用或少使用友元。
类的友元关系是单向的,如果声明B类是A类的友元,B类的成员函数就可以访问A类的私有和保护数据,
但A类的成员函数却不能访问B类的私有、保护数据。
友元函数:
友元函数是在类声明中由关键字friend修饰说明的非成员函数,在它的函数体中能够通过对象名访问 private 和 protected成员
作用:增加灵活性,使程序员可以在封装和快速性方面做合理选择。
访问对象中的成员必须通过对象名。
19.初始化列表,常数据成员
常数据成员必须在构造函数的初始化列表中进行初始化,常成员函数可以被非常对象调用,这样提高了程序的安全性
对于常量型成员变量和引用型成员变量,必须通过参数化列表的方式进行初始化。
20.关于初始化的问题:
const成员:只能在构造函数后的初始化列表中初始化(C++98);
static成员:初始化在类外,且不能加static修饰
const static成员:类只有唯一一份拷贝,且数值不能改变。因此,可以在类中声明处初始化,也可以像static在类外初始化
21.指针:
1.指向常量的指针:不能通过指向常量的指针改变所指对象的值,但指针本身可以改变,可以指向另外的对象。即是只读指针
例如: const int *p1 = &a; //p1是指向常量的指针int a;
const int *p1 = &a; //p1是指向常量的指针
int b;
p1 = &b; //正确,p1本身的值可以改变
*p1 = 1; //编译时出错,不能通过p1改变所指的对象
2.指针类型的常量:若声明指针常量,则指针本身的值不能被改变,即只能指向一个特定的对象,不能指向其他对象
例如: int a;
int * const p2 = &a;
p2 = &b; //错误,p2是指针常量,值不能改变
3.空指针:C++11使用nullptr关键字,是表达更准确,类型安全的空指针
22.指针数组:如果数组元素的类型是指针,这就叫做指针数组
1.如果用一维数组的数组名去初始化指针数组,那么指针数组的数组名可以充当二维数组的数组名使用了,
但是 “用一维数组的数组名去初始化的指针数组” 和 “二维数组” 还是有区别的,二维数组是完全连续的,而指针数组中间可以是间断的
23.引用和指针的区别:
1.引用的大小是所指向的变量的大小,因为引用只是一个别名而已;指针是指针(地址)本身的大小,32位系统下,一般为4个字节。
2.指针可以便捷高效的使用数组
24.指针类型的函数:
1.不要将非静态局部地址用作函数的返回值
错误的例子:在子函数中定义局部变量后将其地址返回给主函数,就是非法地址,因为子函数结束后,里面的局部变量的内存空间被释放了
2.返回的指针要确保在主调函数中是有效、合法的地址
25.指向函数的指针:
1.当将一个函数的地址传给函数指针之后,指针就指向了该函数(指向了该函数的代码),那么就可以将指针当做函数名使用,那么就调用了该函数的函数体了
传不同函数的首地址,这个函数指针就可以充当不同的函数名来使用,去调用相应的函数体,实现了一个函数回调功能
26.常成员函数:
1.常成员函数不能更改类的变量,也不能调用非常成员函数,但是可以调用常成员函数。非常成员函数可以调用常成员函数
2.常成员函数不能用来更新类的成员变量,也不能调用类中未用const修饰的成员函数,只能调用常成员函数。
即常成员函数不能更改类中的成员状态,这与const语义相符。
27.动态内存分配:
0.注意:new 和 delete 是操作符而不是函数
1.动态申请内存操作符 new
new 类型名T(初始化参数列表)
功能:在程序执行期间,申请用于存放T类型对象的内存空间,并依初值列表赋以初值。
结果值:成功:T类型的指针,指向新分配的内存;失败:抛出异常。
2.释放内存操作符delete
delete 指针p
功能:释放指针p所指向的内存。p必须是new操作的返回值。
28.对象指针:
1.对象指针定义形式
类名 *对象指针名;
例:
Point a(5,10);
Piont *ptr;
ptr=&a;
2。通过指针访问对象成员
对象指针名->成员名
例:ptr->getx() 相当于 (*ptr).getx();
29.this指针:
指向当前对象自己
隐含于类的每一个非静态成员函数中。
指出成员函数所操作的对象。
当通过一个对象调用成员函数时,系统先将该对象的地址赋给this指针,然后调用成员函数,成员函数对对象的数据成员进行操作时,就隐含使用了this指针。
例如:Point类的getX函数中的语句:return x;
相当于:return this->x;
30.构造函数:
1.带默认参数的构造函数在声明和定义时,两个地方都需要申明参数,但是在类中申明构造函数时候需要参数默认值
例如: Class X
{
X(int a=5);
}
在实现时候不用再给出默认值:
X::X(int a)
{
…
}
2.C++的默认构造函数与构造函数
今天看书,忽然发现自己对默认构造函数/构造函数的理解很模糊,在实际项目中写类时,这些细节问题并没有涉及到。
因此,就专门对着《C++ Primer Plus》将默认构造函数/构造函数这一块简单总结一下。
构造函数:C++用于构建类的新对象时需要调用的函数,该函数无返回类型!(注意:是“无”! 不是空!(void))。
默认构造函数:未提供显式初始值时,用来穿件对象的构造函数。
以上是二者的定义,但是单从定义很难对二者进行区别,下面依然看代码。
复制代码
1 class testClass
2 {
3 public:
4 testClass(); /* 默认构造函数 */
5 testClass(int a, char b); /* 构造函数 */
6 testClass(int a=10,char b='c'); /* 默认构造函数 */
7
8 private:
9 int m_a;
10 char m_b;
11 };
复制代码
上面的注释中已标出了默认构造函数和构造函数。下面,对二者的区别进行简单直白易懂的总结:
.
1. 默认构造函数主要是用来完成如下形式的初始化的:
1 testClass classA;
2 // 或者 testClass *classA = new testClass;
在这种情况下,如果没有提供默认构造函数,编译器会报错;
非默认构造函数在调用时接受参数,如以下形式:
1 testClass classA(12,'H');
2 //或者 testClass *classA = new testClass(12,'H');
2. 如果程序猿没有定义任何构造函数,则编译器会自动定义默认构造函数,其形式如 testClass() {}; 可以看出,编译器自动提供的默认构造函数是 啥也没有啊 ;
3. 定义默认构造函数有两种方式,如上述代码展示的,一是定义一个无参的构造函数,二是定义所有参数都有默认值的构造函数 ;
4. 注意:一个类只能有一个默认构造函数!也就是说上述两种方式不能同时出现,一般选择 testClass(); 这种形式的默认构造函数 ;
5. 只要程序猿定义了构造函数,编译器就不会再提供默认构造函数了,所以,程序猿最好再手动定义一个默认构造函数,以防出现 testClass a; 这样的错误。
31.vector对象
为什么需要vector?
封装任何类型的动态数组,自动创建和删除。
数组下标越界检查。
例6-18中封装的ArrayOfPoints也提供了类似功能,但只适用于一种类型的数组。
vector对象的定义
vector<元素类型> 数组对象名(数组长度); 其中元素类型也可以是类名,相当于动态创建一个对象数组
例:
vector arr(5)
建立大小为5的int数组
vector对象的使用
对数组元素的引用
与普通数组具有相同形式:
vector对象名 [ 下标表达式 ]
vector数组对象名不表示数组首地址
获得数组长度
用size函数
数组对象名.size()
32.
1.考虑:如何输入整行字符串?
用cin的>>操作符输入字符串,会以空格作为分隔符,空格后的内容会在下一回输入时被读取
输入整行字符串
getline可以输入整行字符串(要包string头文件),例如:
getline(cin, s2);
输入字符串时,可以使用其它分隔符作为字符串结束的标志(例如逗号、分号),将分隔符作为getline的第3个参数即可,例如:
getline(cin, s2, ‘,’);
2.大家百度会发现getline()的原型是istream& getline ( istream &is , string &str , char delim );
其中 istream &is 表示一个输入流,譬如cin;string&str表示把从输入流读入的字符串存放在这个字符串中(可以自己随便命名,str什么的都可以);
char delim表示遇到这个字符停止读入,在不设置的情况下系统默认该字符为’\n’,也就是回车换行符(遇到回车停止读入)。
33.内联函数:
1.内联函数首先是函数,函数的很多性质都适用于内联函数,如内联函数可以重载。
2.在内联函数中不允许使用循环语句和switch结果,带有异常接口声明的函数也不能声明为内联函数。
34.继承:
1.吸收基类成员:
默认情况下派生类包含了全部基类中除构造函数和析构函数之外的所有成员
c++11规定可以用using语句继承基类构造函数
35.不要重新定义继承而来的非虚函数
36.类型转换
公有派生类对象可以被当作基类的对象使用,反之则不可。
派生类的对象可以隐含转换为基类对象;
派生类的对象可以初始化基类的引用;
派生类的指针可以隐含转换为基类的指针。
通过基类对象名、指针只能使用从基类继承的成员。
37.派生类复制构造函数
1.派生类未定义复制构造函数的情况
编译器会在需要时生成一个隐含的复制构造函数;
先调用基类的复制构造函数;
再为派生类新增的成员执行复制。
派生类定义了复制构造函数的情况
2.一般都要为基类的复制构造函数传递参数。
复制构造函数只能接受一个参数,既用来初始化派生类定义的成员,也将被传递给基类的复制构造函数。
基类的复制构造函数形参类型是基类对象的引用,实参可以是派生类对象的引用
例如: C::C(const C &c1): B(c1) {…}
38.访问从基类继承的成员
1.作用域限定
当派生类与基类中有相同成员时:
若未特别限定,则通过派生类对象使用的是派生类中的同名成员。
如要通过派生类对象访问基类中被隐藏的同名成员,应使用基类名和作用域操作符(::)来限定。
2.二义性问题
如果从不同基类继承了同名成员,但是在派生类中没有定义同名成员,“派生类对象名或引用名.成员名”、“派生类指针->成员名”访问成员存在二义性问题
解决方式:用类名限定
39.访问成员的方式有三种:
1.对象名.成员名
2.对象的引用名.成员名
3.对象的指针->成员名
40.虚基类:
1.声明“class B:virtual public A”说明A类是虚基类,同时说明B虚拟继承了基类A。
2.在建立对象时,只有最远派生类的构造函数调用虚基类的构造函数,其他类对虚基类构造函数的调用被忽略。
3.虚基类的声明是在派生类的定义过程中实现的
41.操作符重载:
1.运算符重载为成员函数
重载为类成员的运算符函数定义形式
函数类型 operator 运算符(形参)
{
......
}
参数个数=原操作数个数-1 (后置++、--除外)
2.双目运算符重载规则
如果要重载 B 为类成员函数,使之能够实现表达式 oprd1 B oprd2,其中 oprd1 为A 类对象,则 B 应被重载为 A 类的成员函数,形参类型应该是 oprd2 所属的类型。
经重载后,表达式 oprd1 B oprd2 相当于 oprd1.operator B(oprd2)
42.对象的右值:通过这个值,不能去改变原对象
43.0 虚函数和虚基类是不一样的:
1.虚基类解决的是类成员标识二义性和信息冗余问题
2.而虚函数是实现动态多态性的基础
43.多态,虚函数:
4. typeid注意事项:
1.tyoe_id返回一个tyoe——info对象的引用
2.如果想通过基类的指针获得派生类的数据类型,基类必须带有虚函数
3.只能获取对象的实际类型
-3. dynamic_cast注意事项:
1.**只能应用于指针和引用的转换**
2,要转换的类型中必须包含虚函数
3.转换成功返回子类的地址,失败返回NULL
-2. RTTI :运行时类型识别,必须建立在虚函数的基础之上,否则无需RTTI技术,RTTI技术可以通过父类指针识别器所指向对象的真实数据类型
-1. 意义:通过父类的指针,指向子类对象,然后通过父类指针调用子类对象时用到虚函数
0. C++多态意味着调用成员函数时,会根据调用函数的对象的类型来执行不同的函数;
形成多态必须具备三个条件:
1、必须存在继承关系;
2、继承关系必须有同名虚函数(其中虚函数是在基类中使用关键字Virtual声明的函数,在派生类中重新定义基类中定义的虚函数时,会告诉编译器不要静态链接到该函数);
3、存在基类类型的指针或者引用,通过该指针或引用调用虚函数;
1.不要重写继承而来的非虚函数
虚函数不能声明为内联函数,定义要在类外实现,并且实现的时候不用加virtual关键字
2.初识虚函数
用virtual关键字说明的函数
**虚函数是实现运行时多态性基础**
**C++中的虚函数是动态绑定的函数**
**虚函数必须是非静态的成员函数,虚函数经过派生之后,就可以实现运行过程中的多态。
一般成员函数可以是虚函数
构造函数不能是虚函数
析构函数可以是虚函数**
3.一般虚函数成员
虚函数的声明
virtual 函数类型 函数名(形参表);
虚函数声明只能出现在类定义中的函数原型声明中,而不能在成员函数实现的时候。
在派生类中可以对基类中的成员函数进行覆盖。
虚函数一般不声明为内联函数,因为对虚函数的调用需要动态绑定,而对内联函数的处理是静态的。
4.virtual 关键字
派生类可以不显式地用virtual声明虚函数,这时系统就会用以下规则来判断派生类的一个函数成员是不是虚函数:
该函数是否与基类的虚函数有相同的名称、参数个数及对应参数类型;
该函数是否与基类的虚函数有相同的返回值或者满足类型兼容规则的指针、引用型的返回值;
如果从名称、参数及返回值三个方面检查之后,派生类的函数满足上述条件,就会自动确定为虚函数。这时,派生类的虚函数便覆盖了基类的虚函数。
派生类中的虚函数还会隐藏基类中同名函数的所有其它重载形式。
一般习惯于在派生类的函数中也使用virtual关键字,以增加程序的可读性。
5.虚析构函数是为了避免使用父类指针释放子类对象时造成内存泄露。
6.虚函数表指针:指的是在具有虚函数的情况下,实例化一个对象时,对象的第一块内存中存储的是一个指针,这个指针就是虚函数表的指针
44.虚表与动态绑定
1.虚表
每个多态类有一个虚表(virtual table)
虚表中有当前类的各个虚函数的入口地址
每个对象有一个指向当前类的虚表的指针(虚指针vptr)
2.动态绑定的实现
构造函数中为对象的虚指针赋值
通过多态类型的指针或引用调用成员函数时,通过虚指针找到虚表,进而找到所调用的虚函数的入口地址
通过该入口地址调用虚函数
45.0接口类:仅含有纯虚函数的类,类中不能有其他函数,也不能有数据成员,没有构造函数和析构函数
45抽象类
纯虚函数
纯虚函数是一个在基类中声明的虚函数,它在该基类中没有定义具体的操作内容,要求各派生类根据实际需要定义自己的版本,纯虚函数的声明格式为:
可以规定对外接口的统一形式
virtual 函数类型 函数名(参数表) = 0;
带有纯虚函数的类称为抽象类
抽象类
带有纯虚函数的类称为抽象类:
class 类名 { virtual 类型 函数名(参数表)=0; //其他成员…… }
抽象类作用
抽象类为抽象和设计的目的而声明
将有关的数据和行为组织在一个继承层次结构中,保证派生类具有要求的行为。
对于暂时无法实现的函数,可以声明为纯虚函数,留给派生类去实现。
注意
抽象类只能作为基类来使用。
不能定义抽象类的对象。
46.6.编译预处理命令中< >与“ ”的区别?
答: <>的意思是让编译器在系统编译器的函数库里面找名字相同的头文件.如果没有,编译器就会报错.
"“会让编译器在”"中指定的位置查找.直接写名字的话,会在相对路径里面查找(相对项目文件的路径).
如果没有就会去<>的范围里面查找. 如果两者都没有就会报错
47. double x = p1.getX - p2.getX;
double y = p1.getY - p2.getY;
这样写是错误的,一定不要忘了要打括号,这样很难排查错误
错误 C2296 “-”: 非法,左操作数包含“int (__thiscall Boat:? )(void)”类型
48.继承与封装:
1.你好,总的来说,基类的所有成员被派生类继承后都变成派生类所拥有了,
只是对于继承而来的private成员没办法直接访问而已,这样设计保证了基类private成员的安全性。
如果你希望基类的某些成员被派生类继承且能访问而又不允许外界普通用户代码访问的话,
将基类的那些成员声明为protected就好了
2.类的保护成员在类外不能通过 对象名.成员名 直接访问
49、函数目标:
1.创建一个通用功能的函数,支持多种不同的形参,简化重载函数的函数体设计
2.注意
一个函数模板并非自动可以处理所有类型的数据
只有能够进行函数模板中运算的类型,可以作为类型实参
自定义的类,需要重载模板中的运算符,才能作为类型实参
50.0.定义类模板是不需要生成新的 .cpp 后缀的文件,所有代码都应该写在 .h 文件中
50.为什么有的函数返回引用
1.如果一个函数的返回值是一个对象的值,就是右值,不能成为左值,返回一个右值只能用,不能来改变对象的状态
2.如果返回值为引用,由于引用是对象的别名,通过引用就可以改变对象的值(改变对象的状态),因此是左值
Another:一些零散的知识
1.C++支持两种多态性,其中函数重载和运算符重载用于实现静态多态性
⑨
,而虚函数用于实现
⑩动态多态性
。
2.析构函数无参数,也不可重载,但是可以存在虚析构函数
3.1 内联函数:内联函数是在编译时期展开,而虚函数的特性是运行时才动态联编,所以两者矛盾
要想成为虚函数,必须能够被取到地址.内联函数不能被取到地址所以不能成为虚函数.
你写inline virtual void f(),不能保证函数f()一定是内联的,只能保证f()是虚函数(从而保证此函数一定不是内联函数)
对于问题:
到底内联函数能不能成为虚函数?
答案是不能.问题是你不能够确定一个函数到底是不是inline的.inlien关键字只是对编译器的一个建议:"如果有可能,请把此函数搞成inline的"
3.2 构造函数: 构造函数用来创建一个新的对象,而虚函数的运行是建立在对象的基础上,在构造函数执行时,对象尚未形成
3.3 静态成员函数: 静态成员函数属于一个类而非某一对象,没有this指针,它无法进行对象的判别
3.4 非成员函数
4.区分:int*p=newint(10) 和 int*p=newint[10],前者是动态创建一个int类型的变量,后者是动态创建一个含有10个元素的int类型数组
5.构造函数也可以是私有的
6.动态联编要满足两个条件:被调用的成员函数是虚函数;用指针或引用调用虚函数。
51.泛型程序设计的基本概念:
1.编写不依赖于具体数据类型的程序
2.将算法从特定的数据结构中抽象出来,成为通用的
3.C++的模板为泛型程序设计奠定了关键的基础
4.术语:概念
用来界定具备一定功能的数据类型
5.术语:模型
模型(model):符合一个概念的数据类型称为该概念的模型,例如:
int型是Comparable概念的模型。
6.用概念做模板参数名
很多STL的实现代码就是使用概念来命名模板参数的。
为概念赋予一个名称,并使用该名称作为模板参数名。
52,精度:setprecision操纵符
精度
浮点数输出精度的默认值是6,例如:3466.98。
要改变精度:setprecision操纵符(定义在头文件iomanip中)。
如果不指定fixed或scientific,精度值表示有效数字位数。
如果设置了iosbase::fixed或iosbase::scientific精度值表示小数点之后的位数。
53,键盘也被看做是一个文本文件
54. 常见的异常:数组下标越界,除数为0, 内存不足
55.迭代器:
1.迭代器是泛化的指针,提供了类似指针的操作(诸如++、*、->运算符)
2.迭代器的区间
两个迭代器表示一个区间:[p1, p2)
STL算法常以迭代器的区间作为输入,传递输入数据
合法的区间
p1经过n次(n > 0)自增(++)操作后满足p1 == p2
区间包含p1,但不包含p2
3.迭代器的辅助函数
advance(p, n)
对p执行n次自增操作
distance(first, last)
计算两个迭代器first和last的距离,即对first执行多少次“++”操作后能够使得first == last
56. STL简介
标准模板库(Standard Template Library,简称STL)提供了一些非常常用的数据结构和算法
STL简介
标准模板库(Standard Template Library,简称STL)定义了一套概念体系,为泛型程序设计提供了逻辑基础
STL中的各个类模板、函数模板的参数都是用这个体系中的概念来规定的。
使用STL的模板时,类型参数既可以是C++标准库中已有的类型,也可以是自定义的类型——只要这些类型是所要求概念的模型。
STL的基本组件
容器(container)
迭代器(iterator)
函数对象(function object)
算法(algorithms)
STL的基本组件间的关系
Iterators(迭代器)是算法和容器的桥梁。
将迭代器作为算法的参数、通过迭代器来访问容器而不是把容器直接作为算法的参数。
将函数对象作为算法的参数而不是将函数所执行的运算作为算法的一部分。
使用STL中提供的或自定义的迭代器和函数对象,配合STL的算法,可以组合出各种各样的功能。
STL的基本组件——容器(container)
容纳、包含一组元素的对象。
基本容器类模板
顺序容器
array(数组)、vector(向量)、deque(双端队列)、forward_list(单链表)、list(列表)
(有序)关联容器
set(集合)、multiset(多重集合)、map(映射)、multimap(多重映射)
无序关联容器
unorderedset (无序集合)、unorderedmultiset(无序多重集合)
unorderedmap(无序映射)、unordermultimap(无序多重映射)
容器适配器
stack(栈)、queue(队列)、priority_queue(优先队列)
使用容器,需要包含对应的头文件
STL的基本组件——迭代器(iterator)
迭代器是泛化的指针,提供了顺序访问容器中每个元素的方法
提供了顺序访问容器中每个元素的方法;
可以使用“++”运算符来获得指向下一个元素的迭代器;
可以使用“*”运算符访问一个迭代器所指向的元素,如果元素类型是类或结构体,还可以使用“->”运算符直接访问该元素的一个成员;
有些迭代器还支持通过“–”运算符获得指向上一个元素的迭代器;
迭代器是泛化的指针:指针也具有同样的特性,因此指针本身就是一种迭代器;
使用独立于STL容器的迭代器,需要包含头文件。
STL的基本组件——函数对象(function object)
一个行为类似函数的对象,对它可以像调用函数一样调用。
函数对象是泛化的函数:任何普通的函数和任何重载了“()” 运算符的类的对象都可以作为函数对象使用
使用STL的函数对象,需要包含头文件
STL的基本组件——算法(algorithms)
STL包括70多个算法
例如:排序算法,消除算法,计数算法,比较算法,变换算法,置换算法和容器管理等
可以广泛用于不同的对象和内置的数据类型。
使用STL的算法,需要包含头文件。
例10-1从标准输入读入几个整数,存入向量容器,输出它们的相反数
例10-1:STL程序实例
transform算法的一种实现:
template <class InputIterator, class OutputIterator, class UnaryFunction>
OutputIterator transform(InputIterator first, InputIterator last, OutputIterator result, UnaryFunction op) {
for (;first != last; ++first, ++result)
*result = op(*first);
return result;
}
transform算法顺序遍历first和last两个迭代器所指向的元素;
将每个元素的值作为函数对象op的参数;
将op的返回值通过迭代器result顺序输出;
遍历完成后result迭代器指向的是输出的最后一个元素的下一个位置,transform会将该迭代器返回