模板
1 模板概念及分类
模板是 C++ 提供的 泛型编程 支持
核心目的: 不写重复代码,让类型 做参数
C++ 里模板分两大类:
1️⃣ 函数模板
2️⃣ 类模板
2 函数模板
函数模板作用:
建立一个通用函数,其函数返回值类型和形参类型可以不具体制定,用一个虚拟的类型 来代表
语法:
C++ template < typename T >
函数声明或定义
解释:
template --- 声明创建模板
typename --- 表面其后面的符号是一种数据类型,可以用class代替
T --- 通用的数据类型,名称可以替换,通常为大写字母
示例:
C++ 1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42 // 交换整形函数
void swapInt ( int & a , int & b ){
int temp = a ;
a = b ;
b = temp ;
}
// 交换浮点型函数
void swapDouble ( double & a , double & b ){
double temp = a ;
a = b ;
b = temp ;
}
// 利用模板提供通用的模板函数
template < typename T >
void mySwap ( T & a , T & b ){
T temp = a ;
a = b ;
b = temp ;
}
void test01 (){
int a = 10 ;
int b = 20 ;
swapInt ( a , b );
// 利用模板实现交换
// 1. 自动类型推导
mySwap ( a , b );
// 2. 显示指定类型
mySwap < int > ( a , b );
cout << "a = " << a << endl ;
cout << "b = " << b << endl ;
}
int main (){
test01 ();
return 0 ;
}
总结:
函数模板利用关键字 template
使用函数模板有两种方式:自动类型推导、显示指定类型
模板的目的是为了提高复用性,将类型参数化
2.1 函数模板注意事项
注意事项:
示例:
C++ 1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44 //利用模板提供通用的交换函数
template < class T >
void mySwap ( T & a , T & b )
{
T temp = a ;
a = b ;
b = temp ;
}
// 1、自动类型推导,必须推导出一致的数据类型T,才可以使用
void test01 ()
{
int a = 10 ;
int b = 20 ;
char c = 'c' ;
mySwap ( a , b ); // 正确,可以推导出一致的T
//mySwap(a, c); // 错误,推导不出一致的T类型
}
// 2、模板必须要确定出T的数据类型,才可以使用
template < class T >
void func ()
{
cout << "func 调用" << endl ;
}
void test02 ()
{
//func(); //错误,模板不能独立使用,必须确定出T的类型
func < int > (); //利用显示指定类型的方式,给T一个类型,才可以使用该模板
}
int main () {
test01 ();
test02 ();
system ( "pause" );
return 0 ;
}
总结:
使用模板时必须确定出通用数据类型T,并且能够推导出一致的类型
2.2 函数模板案例
案例描述:
利用函数模板封装一个排序的函数,可以对不同数据类型数组 进行排序
排序规则从大到小,排序算法为选择排序
分别利用char数组 和int数组 进行测试
示例:
C++ 1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65 //交换的函数模板
template < typename T >
void mySwap ( T & a , T & b )
{
T temp = a ;
a = b ;
b = temp ;
}
template < class T > // 也可以替换成typename
//利用选择排序,进行对数组从大到小的排序
void mySort ( T arr [], int len )
{
for ( int i = 0 ; i < len ; i ++ )
{
int max = i ; //最大数的下标
for ( int j = i + 1 ; j < len ; j ++ )
{
if ( arr [ max ] < arr [ j ])
{
max = j ;
}
}
if ( max != i ) //如果最大数的下标不是i,交换两者
{
mySwap ( arr [ max ], arr [ i ]);
}
}
}
template < typename T >
void printArray ( T arr [], int len ) {
for ( int i = 0 ; i < len ; i ++ ) {
cout << arr [ i ] << " " ;
}
cout << endl ;
}
void test01 ()
{
//测试char数组
char charArr [] = "bdcfeagh" ;
int num = sizeof ( charArr ) / sizeof ( char );
mySort ( charArr , num );
printArray ( charArr , num );
}
void test02 ()
{
//测试int数组
int intArr [] = { 7 , 5 , 8 , 1 , 3 , 9 , 2 , 4 , 6 };
int num = sizeof ( intArr ) / sizeof ( int );
mySort ( intArr , num );
printArray ( intArr , num );
}
int main () {
test01 ();
test02 ();
system ( "pause" );
return 0 ;
}
总结:模板可以提高代码复用,需要熟练掌握
2.3 函数模板与普通函数
2.3.1 区别
普通函数调用时可以发生自动类型转换(隐式类型转换)
函数模板调用时,如果利用自动类型推导,不会发生隐式类型转换
如果利用显示指定类型的方式,可以发生隐式类型转换
示例:
C++ 1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35 //普通函数
int myAdd01 ( int a , int b )
{
return a + b ;
}
//函数模板
template < class T >
T myAdd02 ( T a , T b )
{
return a + b ;
}
//使用函数模板时,如果用自动类型推导,不会发生自动类型转换,即隐式类型转换
void test01 ()
{
int a = 10 ;
int b = 20 ;
char c = 'c' ;
cout << myAdd01 ( a , c ) << endl ; //正确,将char类型的'c'隐式转换为int类型 'c' 对应 ASCII码 99
//myAdd02(a, c); // 报错,使用自动类型推导时,不会发生隐式类型转换
myAdd02 < int > ( a , c ); //正确,如果用显示指定类型,可以发生隐式类型转换
}
int main () {
test01 ();
system ( "pause" );
return 0 ;
}
总结:建议使用显示指定类型的方式,调用函数模板,因为可以自己确定通用类型T
2.3.2 调用规则
调用规则如下:
如果函数模板和普通函数都可以实现,优先调用普通函数
可以通过空模板参数列表来强制调用函数模板
函数模板也可以发生重载
如果函数模板可以产生更好的匹配,优先调用函数模板
示例:
C++ 1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47 //普通函数与函数模板调用规则
void myPrint ( int a , int b )
{
cout << "调用的普通函数" << endl ;
}
template < typename T >
void myPrint ( T a , T b )
{
cout << "调用的模板" << endl ;
}
template < typename T >
void myPrint ( T a , T b , T c )
{
cout << "调用重载的模板" << endl ;
}
void test01 ()
{
//1、如果函数模板和普通函数都可以实现,优先调用普通函数
// 注意 如果告诉编译器 普通函数是有的,但只是声明没有实现,或者不在当前文件内实现,就会报错找不到
int a = 10 ;
int b = 20 ;
myPrint ( a , b ); //调用普通函数
//2、可以通过空模板参数列表来强制调用函数模板
myPrint <> ( a , b ); //调用函数模板
//3、函数模板也可以发生重载
int c = 30 ;
myPrint ( a , b , c ); //调用重载的函数模板
//4、 如果函数模板可以产生更好的匹配,优先调用函数模板
char c1 = 'a' ;
char c2 = 'b' ;
myPrint ( c1 , c2 ); //调用函数模板
}
int main () {
test01 ();
system ( "pause" );
return 0 ;
}
既然提供了函数模板,最好就不要提供普通函数,否则容易出现二义性
2.4 函数模板局限性
例如:
C++ template < class T >
void f ( T a , T b )
{
a = b ;
}
在上述代码中提供的赋值操作,如果传入的a和b是一个数组,就无法实现了
再例如:
C++ template < class T >
void f ( T a , T b )
{
if ( a > b ) { ... }
}
在上述代码中,如果T的数据类型传入的是像Person这样的自定义数据类型,也无法正常运行
因此C++为了解决这种问题,提供模板的重载,可以为这些特定的类型 提供具体化的模板
示例:
C++ 1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89 #include <iostream>
using namespace std ;
#include <string>
class Person
{
public :
Person ( string name , int age )
{
this -> m_Name = name ;
this -> m_Age = age ;
}
string m_Name ;
int m_Age ;
};
//普通函数模板
template < class T >
bool myCompare ( T & a , T & b )
{
if ( a == b )
{
return true ;
}
else
{
return false ;
}
}
//具体化,显示具体化的原型和定意思以template<>开头,并通过名称来指出类型
//具体化优先于常规模板
template <> bool myCompare ( Person & p1 , Person & p2 )
{
if ( p1 . m_Name == p2 . m_Name && p1 . m_Age == p2 . m_Age )
{
return true ;
}
else
{
return false ;
}
}
void test01 ()
{
int a = 10 ;
int b = 20 ;
//内置数据类型可以直接使用通用的函数模板
bool ret = myCompare ( a , b );
if ( ret )
{
cout << "a == b " << endl ;
}
else
{
cout << "a != b " << endl ;
}
}
void test02 ()
{
Person p1 ( "Tom" , 10 );
Person p2 ( "Tom" , 10 );
//自定义数据类型,不会调用普通的函数模板
//可以创建具体化的Person数据类型的模板,用于特殊处理这个类型
bool ret = myCompare ( p1 , p2 );
if ( ret )
{
cout << "p1 == p2 " << endl ;
}
else
{
cout << "p1 != p2 " << endl ;
}
}
int main () {
test01 ();
test02 ();
system ( "pause" );
return 0 ;
}
3 类模板
3.1 类模板语法
类模板作用:
建立一个通用类,类中的成员 数据类型可以不具体制定,用一个虚拟的类型 来代表。
语法:
解释:
template --- 声明创建模板
typename --- 表面其后面的符号是一种数据类型,可以用class代替
T --- 通用的数据类型,名称可以替换,通常为大写字母
示例:
C++ 1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35 #include <string>
//类模板
template < class NameType , class AgeType >
class Person
{
public :
Person ( NameType name , AgeType age )
{
this -> mName = name ;
this -> mAge = age ;
}
void showPerson ()
{
cout << "name: " << this -> mName << " age: " << this -> mAge << endl ;
}
public :
NameType mName ;
AgeType mAge ;
};
void test01 ()
{
// 指定NameType 为string类型,AgeType 为 int类型
Person < string , int > P1 ( "孙悟空" , 999 );
P1 . showPerson ();
}
int main () {
test01 ();
system ( "pause" );
return 0 ;
}
C++ template < typename T >
class Box {
private :
T value ;
public :
Box ( T v ) : value ( v ) {}
T get () const { return value ; }
};
Box < int > b1 ( 123 );
Box < std :: string > b2 ( "hello" );
总结:类模板和函数模板语法相似,在声明模板template后面加类,此类称为类模板
3.2 类模板函数模板区别
类模板与函数模板区别主要有两点:
类模板没有自动类型推导的使用方式
类模板在模板参数列表中可以有默认参数
示例:
C++ 1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45 #include <string>
//类模板
template < class NameType , class AgeType = int >
class Person
{
public :
Person ( NameType name , AgeType age )
{
this -> mName = name ;
this -> mAge = age ;
}
void showPerson ()
{
cout << "name: " << this -> mName << " age: " << this -> mAge << endl ;
}
public :
NameType mName ;
AgeType mAge ;
};
//1、类模板没有自动类型推导的使用方式
void test01 ()
{
// Person p("孙悟空", 1000); // 错误 类模板使用时候,不可以用自动类型推导
Person < string , int > p ( "孙悟空" , 1000 ); //必须使用显示指定类型的方式,使用类模板
p . showPerson ();
}
//2、类模板在模板参数列表中可以有默认参数
void test02 ()
{
Person < string > p ( "猪八戒" , 999 ); //类模板中的模板参数列表 可以指定默认参数
p . showPerson ();
}
int main () {
test01 ();
test02 ();
system ( "pause" );
return 0 ;
}
总结:
类模板使用只能用显示指定类型方式
类模板中的模板参数列表可以有默认参数
3.3 类模板成员函数创建时机
类模板中成员函数和普通类中成员函数创建时机是有区别的:
普通类中的成员函数一开始就可以创建
类模板中的成员函数在调用时才创建
示例:
C++ 1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48 class Person1
{
public :
void showPerson1 ()
{
cout << "Person1 show" << endl ;
}
};
class Person2
{
public :
void showPerson2 ()
{
cout << "Person2 show" << endl ;
}
};
template < class T >
class MyClass
{
public :
T obj ;
//类模板中的成员函数,并不是一开始就创建的,而是在模板调用时再生成
void fun1 () { obj . showPerson1 (); }
void fun2 () { obj . showPerson2 (); }
};
void test01 ()
{
MyClass < Person1 > m ;
m . fun1 ();
//m.fun2();//编译会出错,说明函数调用才会去创建成员函数
}
int main () {
test01 ();
system ( "pause" );
return 0 ;
}
总结:类模板中的成员函数并不是一开始就创建的,在调用时才去创建
3.4 类模板对象做函数参数
一共有三种传入方式:
指定传入的类型 --- 直接显示对象的数据类型
参数模板化 --- 将对象中的参数变为模板进行传递
整个类模板化 --- 将这个对象类型 模板化进行传递
示例:
C++ 1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69 #include <string>
//类模板
template < class NameType , class AgeType = int >
class Person
{
public :
Person ( NameType name , AgeType age )
{
this -> mName = name ;
this -> mAge = age ;
}
void showPerson ()
{
cout << "name: " << this -> mName << " age: " << this -> mAge << endl ;
}
public :
NameType mName ;
AgeType mAge ;
};
//1、指定传入的类型
void printPerson1 ( Person < string , int > & p )
{
p . showPerson ();
}
void test01 ()
{
Person < string , int > p ( "孙悟空" , 100 );
printPerson1 ( p );
}
//2、参数模板化
template < class T1 , class T2 >
void printPerson2 ( Person < T1 , T2 >& p )
{
p . showPerson ();
cout << "T1的类型为: " << typeid ( T1 ). name () << endl ;
cout << "T2的类型为: " << typeid ( T2 ). name () << endl ;
}
void test02 ()
{
Person < string , int > p ( "猪八戒" , 90 );
printPerson2 ( p );
}
//3、整个类模板化
template < class T >
void printPerson3 ( T & p )
{
cout << "T的类型为: " << typeid ( T ). name () << endl ;
p . showPerson ();
}
void test03 ()
{
Person < string , int > p ( "唐僧" , 30 );
printPerson3 ( p );
}
int main () {
test01 ();
test02 ();
test03 ();
system ( "pause" );
return 0 ;
}
总结:
通过类模板创建的对象,可以有三种方式向函数中进行传参
使用比较广泛是第一种:指定传入的类型
3.5 类模板与继承
当类模板碰到继承时,需要注意几点:
当子类继承的父类是一个类模板时,子类在声明的时候,要指定出父类中T的类型
如果不指定,编译器无法给子类分配内存
如果想灵活指定出父类中T的类型,子类也需变为类模板
示例:
C++ 1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43 template < class T >
class Base
{
T m ;
};
//class Son:public Base //错误,c++编译需要给子类分配内存,必须知道父类中T的类型才可以向下继承
class Son : public Base < int > //必须指定一个类型
{
};
void test01 ()
{
Son c ;
}
//类模板继承类模板 ,可以用T2指定父类中的T类型
template < class T1 , class T2 >
class Son2 : public Base < T2 >
{
public :
Son2 ()
{
cout << typeid ( T1 ). name () << endl ;
cout << typeid ( T2 ). name () << endl ;
}
};
void test02 ()
{
Son2 < int , char > child1 ;
}
int main () {
test01 ();
test02 ();
system ( "pause" );
return 0 ;
}
总结:如果父类是类模板,子类需要指定出父类中T的数据类型
3.6 类模板成员函数类外实现
示例:
C++ 1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42 #include <string>
//类模板中成员函数类外实现
template < class T1 , class T2 >
class Person {
public :
//成员函数类内声明
Person ( T1 name , T2 age );
void showPerson ();
public :
T1 m_Name ;
T2 m_Age ;
};
//构造函数 类外实现
template < class T1 , class T2 >
Person < T1 , T2 >:: Person ( T1 name , T2 age ) {
this -> m_Name = name ;
this -> m_Age = age ;
}
//成员函数 类外实现
template < class T1 , class T2 >
void Person < T1 , T2 >:: showPerson () {
cout << "姓名: " << this -> m_Name << " 年龄:" << this -> m_Age << endl ;
}
void test01 ()
{
Person < string , int > p ( "Tom" , 20 );
p . showPerson ();
}
int main () {
test01 ();
system ( "pause" );
return 0 ;
}
总结:类模板中成员函数类外实现时,需要加上模板参数列表
3.7 类模板分文件编写
问题:
类模板中成员函数创建时机是在调用阶段,导致分文件编写时链接不到
解决:
解决方式1:直接包含.cpp源文件
解决方式2:将声明和实现写到同一个文件中,并更改后缀名为.hpp,hpp是约定的名称,并不是强制
示例:
person.hpp中代码:
C++ 1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27 #pragma once
#include <iostream>
using namespace std ;
#include <string>
template < class T1 , class T2 >
class Person {
public :
Person ( T1 name , T2 age );
void showPerson ();
public :
T1 m_Name ;
T2 m_Age ;
};
//构造函数 类外实现
template < class T1 , class T2 >
Person < T1 , T2 >:: Person ( T1 name , T2 age ) {
this -> m_Name = name ;
this -> m_Age = age ;
}
//成员函数 类外实现
template < class T1 , class T2 >
void Person < T1 , T2 >:: showPerson () {
cout << "姓名: " << this -> m_Name << " 年龄:" << this -> m_Age << endl ;
}
类模板分文件编写.cpp中代码
C++ 1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22 #include <iostream>
using namespace std ;
//#include "person.h"
#include "person.cpp" //解决方式1,包含cpp源文件
//解决方式2,将声明和实现写到一起,文件后缀名改为.hpp
#include "person.hpp"
void test01 ()
{
Person < string , int > p ( "Tom" , 10 );
p . showPerson ();
}
int main () {
test01 ();
system ( "pause" );
return 0 ;
}
总结:主流的解决方式是第二种,将类模板成员函数写到一起,并将后缀名改为.hpp
3.8 类模板与友元
全局函数类内实现 - 直接在类内声明友元即可
全局函数类外实现 - 需要提前让编译器知道全局函数的存在
示例:
C++ 1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67 #include <string>
//2、全局函数配合友元 类外实现 - 先做函数模板声明,下方在做函数模板定义,在做友元
template < class T1 , class T2 > class Person ;
//如果声明了函数模板,可以将实现写到后面,否则需要将实现体写到类的前面让编译器提前看到
//template<class T1, class T2> void printPerson2(Person<T1, T2> & p);
template < class T1 , class T2 >
void printPerson2 ( Person < T1 , T2 > & p )
{
cout << "类外实现 ---- 姓名: " << p . m_Name << " 年龄:" << p . m_Age << endl ;
}
template < class T1 , class T2 >
class Person
{
//1、全局函数配合友元 类内实现
friend void printPerson ( Person < T1 , T2 > & p )
{
cout << "姓名: " << p . m_Name << " 年龄:" << p . m_Age << endl ;
}
//全局函数配合友元 类外实现
friend void printPerson2 <> ( Person < T1 , T2 > & p );
public :
Person ( T1 name , T2 age )
{
this -> m_Name = name ;
this -> m_Age = age ;
}
private :
T1 m_Name ;
T2 m_Age ;
};
//1、全局函数在类内实现
void test01 ()
{
Person < string , int > p ( "Tom" , 20 );
printPerson ( p );
}
//2、全局函数在类外实现
void test02 ()
{
Person < string , int > p ( "Jerry" , 30 );
printPerson2 ( p );
}
int main () {
//test01();
test02 ();
system ( "pause" );
return 0 ;
}
总结:建议全局函数做类内实现,用法简单,而且编译器可以直接识别
3.9 类模板案例
案例描述: 实现一个通用的数组类,要求如下:
可以对内置数据类型以及自定义数据类型的数据进行存储
将数组中的数据存储到堆区
构造函数中可以传入数组的容量
提供对应的拷贝构造函数以及operator=防止浅拷贝问题
提供尾插法和尾删法对数组中的数据进行增加和删除
可以通过下标的方式访问数组中的元素
可以获取数组中当前元素个数和数组的容量
示例:
myArray.hpp中代码
C++ 1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106 #pragma once
#include <iostream>
using namespace std ;
template < class T >
class MyArray
{
public :
//构造函数
MyArray ( int capacity )
{
this -> m_Capacity = capacity ;
this -> m_Size = 0 ;
pAddress = new T [ this -> m_Capacity ];
}
//拷贝构造
MyArray ( const MyArray & arr )
{
this -> m_Capacity = arr . m_Capacity ;
this -> m_Size = arr . m_Size ;
this -> pAddress = new T [ this -> m_Capacity ];
for ( int i = 0 ; i < this -> m_Size ; i ++ )
{
//如果T为对象,而且还包含指针,必须需要重载 = 操作符,因为这个等号不是 构造 而是赋值,
// 普通类型可以直接= 但是指针类型需要深拷贝
this -> pAddress [ i ] = arr . pAddress [ i ];
}
}
//重载= 操作符 防止浅拷贝问题
MyArray & operator = ( const MyArray & myarray ) {
if ( this -> pAddress != NULL ) {
delete [] this -> pAddress ;
this -> m_Capacity = 0 ;
this -> m_Size = 0 ;
}
this -> m_Capacity = myarray . m_Capacity ;
this -> m_Size = myarray . m_Size ;
this -> pAddress = new T [ this -> m_Capacity ];
for ( int i = 0 ; i < this -> m_Size ; i ++ ) {
this -> pAddress [ i ] = myarray [ i ];
}
return * this ;
}
//重载[] 操作符 arr[0]
T & operator []( int index )
{
return this -> pAddress [ index ]; //不考虑越界,用户自己去处理
}
//尾插法
void Push_back ( const T & val )
{
if ( this -> m_Capacity == this -> m_Size )
{
return ;
}
this -> pAddress [ this -> m_Size ] = val ;
this -> m_Size ++ ;
}
//尾删法
void Pop_back ()
{
if ( this -> m_Size == 0 )
{
return ;
}
this -> m_Size -- ;
}
//获取数组容量
int getCapacity ()
{
return this -> m_Capacity ;
}
//获取数组大小
int getSize ()
{
return this -> m_Size ;
}
//析构
~ MyArray ()
{
if ( this -> pAddress != NULL )
{
delete [] this -> pAddress ;
this -> pAddress = NULL ;
this -> m_Capacity = 0 ;
this -> m_Size = 0 ;
}
}
private :
T * pAddress ; //指向一个堆空间,这个空间存储真正的数据
int m_Capacity ; //容量
int m_Size ; // 大小
};
类模板案例—数组类封装.cpp中
C++ 1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88 #include "myArray.hpp"
#include <string>
void printIntArray ( MyArray < int >& arr ) {
for ( int i = 0 ; i < arr . getSize (); i ++ ) {
cout << arr [ i ] << " " ;
}
cout << endl ;
}
//测试内置数据类型
void test01 ()
{
MyArray < int > array1 ( 10 );
for ( int i = 0 ; i < 10 ; i ++ )
{
array1 . Push_back ( i );
}
cout << "array1打印输出:" << endl ;
printIntArray ( array1 );
cout << "array1的大小:" << array1 . getSize () << endl ;
cout << "array1的容量:" << array1 . getCapacity () << endl ;
cout << "--------------------------" << endl ;
MyArray < int > array2 ( array1 );
array2 . Pop_back ();
cout << "array2打印输出:" << endl ;
printIntArray ( array2 );
cout << "array2的大小:" << array2 . getSize () << endl ;
cout << "array2的容量:" << array2 . getCapacity () << endl ;
}
//测试自定义数据类型
class Person {
public :
Person () {}
Person ( string name , int age ) {
this -> m_Name = name ;
this -> m_Age = age ;
}
public :
string m_Name ;
int m_Age ;
};
void printPersonArray ( MyArray < Person >& personArr )
{
for ( int i = 0 ; i < personArr . getSize (); i ++ ) {
cout << "姓名:" << personArr [ i ]. m_Name << " 年龄: " << personArr [ i ]. m_Age << endl ;
}
}
void test02 ()
{
//创建数组
MyArray < Person > pArray ( 10 );
Person p1 ( "孙悟空" , 30 );
Person p2 ( "韩信" , 20 );
Person p3 ( "妲己" , 18 );
Person p4 ( "王昭君" , 15 );
Person p5 ( "赵云" , 24 );
//插入数据
pArray . Push_back ( p1 );
pArray . Push_back ( p2 );
pArray . Push_back ( p3 );
pArray . Push_back ( p4 );
pArray . Push_back ( p5 );
printPersonArray ( pArray );
cout << "pArray的大小:" << pArray . getSize () << endl ;
cout << "pArray的容量:" << pArray . getCapacity () << endl ;
}
int main () {
//test01();
test02 ();
system ( "pause" );
return 0 ;
}
4 STL
STL(Standard Template Library,标准模板库 )
STL 从广义上分为: 容器(container) 算法(algorithm) 迭代器(iterator)
容器 和算法 之间通过迭代器 进行无缝连接。
STL 几乎所有的代码都采用了模板类或者模板函数
4.1 STL六大组件
STL大体分为六大组件,分别是:容器、算法、迭代器、仿函数、适配器(配接器)、空间配置器
容器:各种数据结构,如vector、list、deque、set、map等,用来存放数据。
算法:各种常用的算法,如sort、find、copy、for_each等
迭代器:扮演了容器与算法之间的胶合剂。
仿函数:行为类似函数,可作为算法的某种策略。
适配器:一种用来修饰容器或者仿函数或迭代器接口的东西。
空间配置器:负责空间的配置与管理。
4.2 STL中的容器、算法、迭代器
容器: 置物之所也
STL容器 就是将运用最广泛的一些数据结构 实现出来
常用的数据结构:数组, 链表,树, 栈, 队列, 集合, 映射表 等
这些容器分为序列式容器 和关联式容器 两种:
序列式容器 :强调值的排序,序列式容器中的每个元素均有固定的位置。
关联式容器 :二叉树结构,各元素之间没有严格的物理上的顺序关系
算法: 问题之解法也
有限的步骤,解决逻辑或数学上的问题,这一门学科我们叫做算法(Algorithms)
算法分为:质变算法 和非质变算法 。
质变算法:是指运算过程中会更改区间内的元素的内容。例如拷贝,替换,删除等等
非质变算法:是指运算过程中不会更改区间内的元素内容,例如查找、计数、遍历、寻找极值等等
迭代器: 容器和算法之间粘合剂
提供一种方法,使之能够依序寻访某个容器所含的各个元素,而又无需暴露该容器的内部表示方式。
每个容器都有自己专属的迭代器
迭代器使用非常类似于指针,初学阶段我们可以先理解迭代器为指针
迭代器种类:
种类
功能
支持运算
输入迭代器
对数据的只读访问
只读,支持++、==、!=
输出迭代器
对数据的只写访问
只写,支持++
前向迭代器
读写操作,并能向前推进迭代器
读写,支持++、==、!=
双向迭代器
读写操作,并能向前和向后操作
读写,支持++、--,
随机访问迭代器
读写操作,可以以跳跃的方式访问任意数据,功能最强的迭代器
读写,支持++、--、[n]、-n、<、<=、>、>=
常用的容器中迭代器种类为双向迭代器,和随机访问迭代器
4.3 string 容器
4.3.1 string基本概念
本质:
string是C++风格的字符串,而string本质上是一个类
string和char * 区别:
char * 是一个指针
string是一个类,类内部封装了char*,管理这个字符串,是一个char*型的容器。
特点:
string 类内部封装了很多成员方法
例如:查找find,拷贝copy,删除delete 替换replace,插入insert
string管理char*所分配的内存,不用担心复制越界和取值越界等,由类内部进行负责
4.3.2 string构造函数
构造函数原型:
string();
//创建一个空的字符串 例如: string str;
string(const char* s);
//使用字符串s初始化
string(const string& str);
//使用一个string对象初始化另一个string对象
string(int n, char c);
//使用n个字符c初始化
示例:
C++ 1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27 #include <string>
//string构造
void test01 ()
{
string s1 ; //创建空字符串,调用无参构造函数
cout << "str1 = " << s1 << endl ;
const char * str = "hello world" ;
string s2 ( str ); //把c_string转换成了string
cout << "str2 = " << s2 << endl ;
string s3 ( s2 ); //调用拷贝构造函数
cout << "str3 = " << s3 << endl ;
string s4 ( 10 , 'a' );
cout << "str3 = " << s3 << endl ;
}
int main () {
test01 ();
system ( "pause" );
return 0 ;
}
总结:string的多种构造方式没有可比性,灵活使用即可
4.3.3 string 赋值操作
功能描述:
赋值的函数原型:
string& operator=(const char* s);
//char*类型字符串 赋值给当前的字符串
string& operator=(const string &s);
//把字符串s赋给当前的字符串
string& operator=(char c);
//字符赋值给当前的字符串
string& assign(const char *s);
//把字符串s赋给当前的字符串
string& assign(const char *s, int n);
//把字符串s的前n个字符赋给当前的字符串
string& assign(const string &s);
//把字符串s赋给当前字符串
string& assign(int n, char c);
//用n个字符c赋给当前字符串
示例:
C++ 1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41 //赋值
void test01 ()
{
string str1 ;
str1 = "hello world" ;
cout << "str1 = " << str1 << endl ;
string str2 ;
str2 = str1 ;
cout << "str2 = " << str2 << endl ;
string str3 ;
str3 = 'a' ;
cout << "str3 = " << str3 << endl ;
string str4 ;
str4 . assign ( "hello c++" );
cout << "str4 = " << str4 << endl ;
string str5 ;
str5 . assign ( "hello c++" , 5 );
cout << "str5 = " << str5 << endl ;
string str6 ;
str6 . assign ( str5 );
cout << "str6 = " << str6 << endl ;
string str7 ;
str7 . assign ( 5 , 'x' );
cout << "str7 = " << str7 << endl ;
}
int main () {
test01 ();
system ( "pause" );
return 0 ;
}
总结:
string的赋值方式很多,operator=
这种方式是比较实用的
4.3.4 string字符串拼接
*功能描述:
函数原型:
string& operator+=(const char* str);
//重载+=操作符
string& operator+=(const char c);
//重载+=操作符
string& operator+=(const string& str);
//重载+=操作符
string& append(const char *s);
//把字符串s连接到当前字符串结尾
string& append(const char *s, int n);
//把字符串s的前n个字符连接到当前字符串结尾
string& append(const string &s);
//同operator+=(const string& str)
string& append(const string &s, int pos, int n);
//字符串s中从pos开始的n个字符连接到字符串结尾
示例:
C++ 1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34 //字符串拼接
void test01 ()
{
string str1 = "我" ;
str1 += "爱玩游戏" ;
cout << "str1 = " << str1 << endl ;
str1 += ':' ;
cout << "str1 = " << str1 << endl ;
string str2 = "LOL DNF" ;
str1 += str2 ;
cout << "str1 = " << str1 << endl ;
string str3 = "I" ;
str3 . append ( " love " );
str3 . append ( "game abcde" , 4 );
//str3.append(str2);
str3 . append ( str2 , 4 , 3 ); // 从下标4位置开始 ,截取3个字符,拼接到字符串末尾
cout << "str3 = " << str3 << endl ;
}
int main () {
test01 ();
system ( "pause" );
return 0 ;
}
总结:字符串拼接的重载版本很多,初学阶段记住几种即可
4.3.5 string查找和替换
功能描述:
查找:查找指定字符串是否存在
替换:在指定的位置替换字符串
函数原型:
int find(const string& str, int pos = 0) const;
//查找str第一次出现位置,从pos开始查找
int find(const char* s, int pos = 0) const;
//查找s第一次出现位置,从pos开始查找
int find(const char* s, int pos, int n) const;
//从pos位置查找s的前n个字符第一次位置
int find(const char c, int pos = 0) const;
//查找字符c第一次出现位置
int rfind(const string& str, int pos = npos) const;
//查找str最后一次位置,从pos开始查找
int rfind(const char* s, int pos = npos) const;
//查找s最后一次出现位置,从pos开始查找
int rfind(const char* s, int pos, int n) const;
//从pos查找s的前n个字符最后一次位置
int rfind(const char c, int pos = 0) const;
//查找字符c最后一次出现位置
string& replace(int pos, int n, const string& str);
//替换从pos开始n个字符为字符串str
string& replace(int pos, int n,const char* s);
//替换从pos开始的n个字符为字符串s
示例:
C++ 1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42 //查找和替换
void test01 ()
{
//查找
string str1 = "abcdefgde" ;
int pos = str1 . find ( "de" );
if ( pos == -1 )
{
cout << "未找到" << endl ;
}
else
{
cout << "pos = " << pos << endl ;
}
pos = str1 . rfind ( "de" );
cout << "pos = " << pos << endl ;
}
void test02 ()
{
//替换
string str1 = "abcdefgde" ;
str1 . replace ( 1 , 3 , "1111" );
cout << "str1 = " << str1 << endl ;
}
int main () {
//test01();
//test02();
system ( "pause" );
return 0 ;
}
总结:
find查找是从左往后,rfind从右往左
find找到字符串后返回查找的第一个字符位置,找不到返回-1
replace在替换时,要指定从哪个位置起,多少个字符,替换成什么样的字符串
4.4 vector容器
功能:
vector数据结构和数组非常相似 ,也称为单端数组
vector与普通数组区别:
不同之处在于数组是静态空间,而vector可以动态扩展
动态扩展:
4.4.1 vector构造函数
功能描述:
函数原型:
vector<T> v;
//采用模板实现类实现,默认构造函数
vector(v.begin(), v.end());
//将v[begin(), end())区间中的元素拷贝给本身。
vector(n, elem);
//构造函数将n个elem拷贝给本身。
vector(const vector &vec);
//拷贝构造函数。
示例:
C++ 1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37 #include <vector>
void printVector ( vector < int >& v ) {
for ( vector < int >:: iterator it = v . begin (); it != v . end (); it ++ ) {
cout << * it << " " ;
}
cout << endl ;
}
void test01 ()
{
vector < int > v1 ; //无参构造
for ( int i = 0 ; i < 10 ; i ++ )
{
v1 . push_back ( i );
}
printVector ( v1 );
vector < int > v2 ( v1 . begin (), v1 . end ());
printVector ( v2 );
vector < int > v3 ( 10 , 100 );
printVector ( v3 );
vector < int > v4 ( v3 );
printVector ( v4 );
}
int main () {
test01 ();
system ( "pause" );
return 0 ;
}
总结: vector的多种构造方式没有可比性,灵活使用即可
4.4.2 vector赋值操作
功能描述:
函数原型:
vector& operator=(const vector &vec);
//重载等号操作符
assign(beg, end);
//将[beg, end)区间中的数据拷贝赋值给本身。
assign(n, elem);
//将n个elem拷贝赋值给本身。
示例:
C++ 1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41 #include <vector>
void printVector ( vector < int >& v ) {
for ( vector < int >:: iterator it = v . begin (); it != v . end (); it ++ ) {
cout << * it << " " ;
}
cout << endl ;
}
//赋值操作
void test01 ()
{
vector < int > v1 ; //无参构造
for ( int i = 0 ; i < 10 ; i ++ )
{
v1 . push_back ( i );
}
printVector ( v1 );
vector < int > v2 ;
v2 = v1 ;
printVector ( v2 );
vector < int > v3 ;
v3 . assign ( v1 . begin (), v1 . end ());
printVector ( v3 );
vector < int > v4 ;
v4 . assign ( 10 , 100 );
printVector ( v4 );
}
int main () {
test01 ();
system ( "pause" );
return 0 ;
}
总结: vector赋值方式比较简单,使用operator=,或者assign都可以
4.4.3 vector容量和大小
功能描述:
函数原型:
//如果容器变短,则末尾超出容器长度的元素被删除。
resize(int num, elem);
//重新指定容器的长度为num,若容器变长,则以elem值填充新位置。
//如果容器变短,则末尾超出容器长度的元素被删除
C++ 1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46 #include <vector>
void printVector ( vector < int >& v ) {
for ( vector < int >:: iterator it = v . begin (); it != v . end (); it ++ ) {
cout << * it << " " ;
}
cout << endl ;
}
void test01 ()
{
vector < int > v1 ;
for ( int i = 0 ; i < 10 ; i ++ )
{
v1 . push_back ( i );
}
printVector ( v1 );
if ( v1 . empty ())
{
cout << "v1为空" << endl ;
}
else
{
cout << "v1不为空" << endl ;
cout << "v1的容量 = " << v1 . capacity () << endl ;
cout << "v1的大小 = " << v1 . size () << endl ;
}
//resize 重新指定大小 ,若指定的更大,默认用0填充新位置,可以利用重载版本替换默认填充
v1 . resize ( 15 , 10 );
printVector ( v1 );
//resize 重新指定大小 ,若指定的更小,超出部分元素被删除
v1 . resize ( 5 );
printVector ( v1 );
}
int main () {
test01 ();
system ( "pause" );
return 0 ;
}
总结:
判断是否为空 --- empty
返回元素个数 --- size
返回容器容量 --- capacity
重新指定大小 --- resize
4.4.4 vector插入和删除
功能描述:
函数原型:
push_back(ele);
//尾部插入元素ele
pop_back();
//删除最后一个元素
insert(const_iterator pos, ele);
//迭代器指向位置pos插入元素ele
insert(const_iterator pos, int count,ele);
//迭代器指向位置pos插入count个元素ele
erase(const_iterator pos);
//删除迭代器指向的元素
erase(const_iterator start, const_iterator end);
//删除迭代器从start到end之间的元素
clear();
//删除容器中所有元素
示例:
C++ 1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49 #include <vector>
void printVector ( vector < int >& v ) {
for ( vector < int >:: iterator it = v . begin (); it != v . end (); it ++ ) {
cout << * it << " " ;
}
cout << endl ;
}
//插入和删除
void test01 ()
{
vector < int > v1 ;
//尾插
v1 . push_back ( 10 );
v1 . push_back ( 20 );
v1 . push_back ( 30 );
v1 . push_back ( 40 );
v1 . push_back ( 50 );
printVector ( v1 );
//尾删
v1 . pop_back ();
printVector ( v1 );
//插入
v1 . insert ( v1 . begin (), 100 );
printVector ( v1 );
v1 . insert ( v1 . begin (), 2 , 1000 );
printVector ( v1 );
//删除
v1 . erase ( v1 . begin ());
printVector ( v1 );
//清空
v1 . erase ( v1 . begin (), v1 . end ());
v1 . clear ();
printVector ( v1 );
}
int main () {
test01 ();
system ( "pause" );
return 0 ;
}
总结:
尾插 --- push_back
尾删 --- pop_back
插入 --- insert (位置迭代器)
删除 --- erase (位置迭代器)
清空 --- clear
4.4.5 vector数据存取
功能描述:
函数原型:
at(int idx);
//返回索引idx所指的数据
operator[];
//返回索引idx所指的数据
front();
//返回容器中第一个数据元素
back();
//返回容器中最后一个数据元素
示例:
C++ 1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34 #include <vector>
void test01 ()
{
vector < int > v1 ;
for ( int i = 0 ; i < 10 ; i ++ )
{
v1 . push_back ( i );
}
for ( int i = 0 ; i < v1 . size (); i ++ )
{
cout << v1 [ i ] << " " ;
}
cout << endl ;
for ( int i = 0 ; i < v1 . size (); i ++ )
{
cout << v1 . at ( i ) << " " ;
}
cout << endl ;
cout << "v1的第一个元素为: " << v1 . front () << endl ;
cout << "v1的最后一个元素为: " << v1 . back () << endl ;
}
int main () {
test01 ();
system ( "pause" );
return 0 ;
}
总结:
除了用迭代器获取vector容器中元素,[ ]和at也可以
front返回容器第一个元素
back返回容器最后一个元素
4.4.6 vector互换容器
功能描述:
函数原型:
swap(vec);
// 将vec与本身的元素互换
示例:
C++ 1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65 #include <vector>
void printVector ( vector < int >& v ) {
for ( vector < int >:: iterator it = v . begin (); it != v . end (); it ++ ) {
cout << * it << " " ;
}
cout << endl ;
}
void test01 ()
{
vector < int > v1 ;
for ( int i = 0 ; i < 10 ; i ++ )
{
v1 . push_back ( i );
}
printVector ( v1 );
vector < int > v2 ;
for ( int i = 10 ; i > 0 ; i -- )
{
v2 . push_back ( i );
}
printVector ( v2 );
//互换容器
cout << "互换后" << endl ;
v1 . swap ( v2 );
printVector ( v1 );
printVector ( v2 );
}
void test02 ()
{
vector < int > v ;
for ( int i = 0 ; i < 100000 ; i ++ ) {
v . push_back ( i );
}
cout << "v的容量为:" << v . capacity () << endl ;
cout << "v的大小为:" << v . size () << endl ;
v . resize ( 3 );
cout << "v的容量为:" << v . capacity () << endl ;
cout << "v的大小为:" << v . size () << endl ;
//收缩内存
vector < int > ( v ). swap ( v ); //匿名对象
cout << "v的容量为:" << v . capacity () << endl ;
cout << "v的大小为:" << v . size () << endl ;
}
int main () {
test01 ();
test02 ();
system ( "pause" );
return 0 ;
}
总结:swap可以使两个容器互换,可以达到实用的收缩内存效果
4.4.7 vector预留空间
功能描述:
函数原型:
reserve(int len);
//容器预留len个元素长度,预留位置不初始化,元素不可访问。
示例:
C++ 1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30 #include <vector>
void test01 ()
{
vector < int > v ;
//预留空间
v . reserve ( 100000 );
int num = 0 ;
int * p = NULL ;
for ( int i = 0 ; i < 100000 ; i ++ ) {
v . push_back ( i );
if ( p != & v [ 0 ]) {
p = & v [ 0 ];
num ++ ;
}
}
cout << "num:" << num << endl ;
}
int main () {
test01 ();
system ( "pause" );
return 0 ;
}
总结:如果数据量较大,可以一开始利用reserve预留空间
4.5 deque容器
4.5.1 deque容器基本概念
功能:
deque内部有个中控器 ,维护每段缓冲区中的内容,缓冲区中存放真实数据
中控器维护的是每个缓冲区的地址,使得使用deque时像一片连续的内存空间
vector 的内存是一块连续的大内存,像数组一样排好队。
插入/删除只能在尾部高效(否则就要整体搬家)。
deque 是把元素分段放在多个小块缓冲区(buffer) 里,不要求物理连续。
可以在头部和尾部都快速插入/删除。
通过一个 中控器(map,也叫控制中心) 把这些小块串起来,看起来像一片连续的空间。
4.5.2 deque构造函数
功能描述:
函数原型:
deque<T>
deqT; //默认构造形式
deque(beg, end);
//构造函数将[beg, end)区间中的元素拷贝给本身。
deque(n, elem);
//构造函数将n个elem拷贝给本身。
deque(const deque &deq);
//拷贝构造函数
示例:
C++ 1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37 #include <deque>
void printDeque ( const deque < int >& d )
{
for ( deque < int >:: const_iterator it = d . begin (); it != d . end (); it ++ ) {
cout << * it << " " ;
}
cout << endl ;
}
//deque构造
void test01 () {
deque < int > d1 ; //无参构造函数
for ( int i = 0 ; i < 10 ; i ++ )
{
d1 . push_back ( i );
}
printDeque ( d1 );
deque < int > d2 ( d1 . begin (), d1 . end ());
printDeque ( d2 );
deque < int > d3 ( 10 , 100 );
printDeque ( d3 );
deque < int > d4 = d3 ;
printDeque ( d4 );
}
int main () {
test01 ();
system ( "pause" );
return 0 ;
}
总结: deque容器和vector容器的构造方式几乎一致,灵活使用即可
4.5.3 deque赋值操作
功能描述:
函数原型:
deque& operator=(const deque &deq);
//重载等号操作符
assign(beg, end);
//将[beg, end)区间中的数据拷贝赋值给本身。
assign(n, elem);
//将n个elem拷贝赋值给本身。
示例:
C++ 1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42 #include <deque>
void printDeque ( const deque < int >& d )
{
for ( deque < int >:: const_iterator it = d . begin (); it != d . end (); it ++ ) {
cout << * it << " " ;
}
cout << endl ;
}
//赋值操作
void test01 ()
{
deque < int > d1 ;
for ( int i = 0 ; i < 10 ; i ++ )
{
d1 . push_back ( i );
}
printDeque ( d1 );
deque < int > d2 ;
d2 = d1 ;
printDeque ( d2 );
deque < int > d3 ;
d3 . assign ( d1 . begin (), d1 . end ());
printDeque ( d3 );
deque < int > d4 ;
d4 . assign ( 10 , 100 );
printDeque ( d4 );
}
int main () {
test01 ();
system ( "pause" );
return 0 ;
}
总结:deque赋值操作也与vector相同,需熟练掌握
4.5.4 deque大小操作
功能描述:
函数原型:
deque.empty();
//判断容器是否为空
deque.size();
//返回容器中元素的个数
deque.resize(num);
//重新指定容器的长度为num,若容器变长,则以默认值填充新位置。
//如果容器变短,则末尾超出容器长度的元素被删除。
deque.resize(num, elem);
//重新指定容器的长度为num,若容器变长,则以elem值填充新位置。
//如果容器变短,则末尾超出容器长度的元素被删除。
示例:
C++ 1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47 #include <deque>
void printDeque ( const deque < int >& d )
{
for ( deque < int >:: const_iterator it = d . begin (); it != d . end (); it ++ ) {
cout << * it << " " ;
}
cout << endl ;
}
//大小操作
void test01 ()
{
deque < int > d1 ;
for ( int i = 0 ; i < 10 ; i ++ )
{
d1 . push_back ( i );
}
printDeque ( d1 );
//判断容器是否为空
if ( d1 . empty ()) {
cout << "d1为空!" << endl ;
}
else {
cout << "d1不为空!" << endl ;
//统计大小
cout << "d1的大小为:" << d1 . size () << endl ;
}
//重新指定大小
d1 . resize ( 15 , 1 );
printDeque ( d1 );
d1 . resize ( 5 );
printDeque ( d1 );
}
int main () {
test01 ();
system ( "pause" );
return 0 ;
}
总结:
deque没有容量的概念
判断是否为空 --- empty
返回元素个数 --- size
重新指定个数 --- resize
4.5.5 deque插入和删除
功能描述:
函数原型:
两端插入操作:
push_back(elem);
//在容器尾部添加一个数据
push_front(elem);
//在容器头部插入一个数据
pop_back();
//删除容器最后一个数据
pop_front();
//删除容器第一个数据
指定位置操作:
insert(pos,elem);
//在pos位置插入一个elem元素的拷贝,返回新数据的位置。
insert(pos,n,elem);
//在pos位置插入n个elem数据,无返回值。
insert(pos,beg,end);
//在pos位置插入[beg,end)区间的数据,无返回值。
clear();
//清空容器的所有数据
erase(beg,end);
//删除[beg,end)区间的数据,返回下一个数据的位置。
erase(pos);
//删除pos位置的数据,返回下一个数据的位置。
示例:
C++ 1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86 #include <deque>
void printDeque ( const deque < int >& d )
{
for ( deque < int >:: const_iterator it = d . begin (); it != d . end (); it ++ ) {
cout << * it << " " ;
}
cout << endl ;
}
//两端操作
void test01 ()
{
deque < int > d ;
//尾插
d . push_back ( 10 );
d . push_back ( 20 );
//头插
d . push_front ( 100 );
d . push_front ( 200 );
printDeque ( d );
//尾删
d . pop_back ();
//头删
d . pop_front ();
printDeque ( d );
}
//插入
void test02 ()
{
deque < int > d ;
d . push_back ( 10 );
d . push_back ( 20 );
d . push_front ( 100 );
d . push_front ( 200 );
printDeque ( d );
d . insert ( d . begin (), 1000 );
printDeque ( d );
d . insert ( d . begin (), 2 , 10000 );
printDeque ( d );
deque < int > d2 ;
d2 . push_back ( 1 );
d2 . push_back ( 2 );
d2 . push_back ( 3 );
d . insert ( d . begin (), d2 . begin (), d2 . end ());
printDeque ( d );
}
//删除
void test03 ()
{
deque < int > d ;
d . push_back ( 10 );
d . push_back ( 20 );
d . push_front ( 100 );
d . push_front ( 200 );
printDeque ( d );
d . erase ( d . begin ());
printDeque ( d );
d . erase ( d . begin (), d . end ());
d . clear ();
printDeque ( d );
}
int main () {
//test01();
//test02();
test03 ();
system ( "pause" );
return 0 ;
}
总结:
插入和删除提供的位置是迭代器!
尾插 --- push_back
尾删 --- pop_back
头插 --- push_front
头删 --- pop_front
4.5.6 deque数据存取
功能描述:
函数原型:
at(int idx);
//返回索引idx所指的数据
operator[];
//返回索引idx所指的数据
front();
//返回容器中第一个数据元素
back();
//返回容器中最后一个数据元素
示例:
C++ 1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46 #include <deque>
void printDeque ( const deque < int >& d )
{
for ( deque < int >:: const_iterator it = d . begin (); it != d . end (); it ++ ) {
cout << * it << " " ;
}
cout << endl ;
}
//数据存取
void test01 ()
{
deque < int > d ;
d . push_back ( 10 );
d . push_back ( 20 );
d . push_front ( 100 );
d . push_front ( 200 );
for ( int i = 0 ; i < d . size (); i ++ ) {
cout << d [ i ] << " " ;
}
cout << endl ;
for ( int i = 0 ; i < d . size (); i ++ ) {
cout << d . at ( i ) << " " ;
}
cout << endl ;
cout << "front:" << d . front () << endl ;
cout << "back:" << d . back () << endl ;
}
int main () {
test01 ();
system ( "pause" );
return 0 ;
}
总结:
除了用迭代器获取deque容器中元素,[ ]和at也可以
front返回容器第一个元素
back返回容器最后一个元素
4.5.7 deque排序
功能描述:
算法:
sort(iterator beg, iterator end)
//对beg和end区间内元素进行排序
示例:
C++ 1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35 #include <deque>
#include <algorithm>
void printDeque ( const deque < int >& d )
{
for ( deque < int >:: const_iterator it = d . begin (); it != d . end (); it ++ ) {
cout << * it << " " ;
}
cout << endl ;
}
void test01 ()
{
deque < int > d ;
d . push_back ( 10 );
d . push_back ( 20 );
d . push_front ( 100 );
d . push_front ( 200 );
printDeque ( d );
sort ( d . begin (), d . end ());
printDeque ( d );
}
int main () {
test01 ();
system ( "pause" );
return 0 ;
}
总结:sort算法非常实用,使用时包含头文件 algorithm即可
4.6 stack容器
4.6.1 stack 基本概念
概念: stack是一种先进后出 (First In Last Out,FILO)的数据结构,它只有一个出口
栈中只有顶端的元素才可以被外界使用,因此栈不允许有遍历行为
栈中进入数据称为 --- 入栈 push
栈中弹出数据称为 --- 出栈 pop
4.6.2 stack 常用接口
功能描述:栈容器常用的对外接口
构造函数:
stack<T> stk;
//stack采用模板类实现, stack对象的默认构造形式
stack(const stack &stk);
//拷贝构造函数
赋值操作:
stack& operator=(const stack &stk);
//重载等号操作符
数据存取:
push(elem);
//向栈顶添加元素
pop();
//从栈顶移除第一个元素
top();
//返回栈顶元素
大小操作:
empty();
//判断堆栈是否为空
size();
//返回栈的大小
示例:
C++ 1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31 #include <stack>
//栈容器常用接口
void test01 ()
{
//创建栈容器 栈容器必须符合先进后出
stack < int > s ;
//向栈中添加元素,叫做 压栈 入栈
s . push ( 10 );
s . push ( 20 );
s . push ( 30 );
while ( ! s . empty ()) {
//输出栈顶元素
cout << "栈顶元素为: " << s . top () << endl ;
//弹出栈顶元素
s . pop ();
}
cout << "栈的大小为:" << s . size () << endl ;
}
int main () {
test01 ();
system ( "pause" );
return 0 ;
}
总结:
入栈 --- push
出栈 --- pop
返回栈顶 --- top
判断栈是否为空 --- empty
返回栈大小 --- size
4.7 queue容器
4.7.1 queue基本概念
概念: Queue是一种先进先出 (First In First Out,FIFO)的数据结构,它有两个出口
队列容器允许从一端新增元素,从另一端移除元素
队列中只有队头和队尾才可以被外界使用,因此队列不允许有遍历行为
队列中进数据称为 --- 入队 push
队列中出数据称为 --- 出队 pop
4.7.2 queue常用接口
功能描述:栈容器常用的对外接口
构造函数:
queue<T> que;
//queue采用模板类实现,queue对象的默认构造形式
queue(const queue &que);
//拷贝构造函数
赋值操作:
queue& operator=(const queue &que);
//重载等号操作符
数据存取:
push(elem);
//往队尾添加元素
pop();
//从队头移除第一个元素
back();
//返回最后一个元素
front();
//返回第一个元素
大小操作:
empty();
//判断堆栈是否为空
size();
//返回栈的大小
示例:
C++ 1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57 #include <queue>
#include <string>
class Person
{
public :
Person ( string name , int age )
{
this -> m_Name = name ;
this -> m_Age = age ;
}
string m_Name ;
int m_Age ;
};
void test01 () {
//创建队列
queue < Person > q ;
//准备数据
Person p1 ( "唐僧" , 30 );
Person p2 ( "孙悟空" , 1000 );
Person p3 ( "猪八戒" , 900 );
Person p4 ( "沙僧" , 800 );
//向队列中添加元素 入队操作
q . push ( p1 );
q . push ( p2 );
q . push ( p3 );
q . push ( p4 );
//队列不提供迭代器,更不支持随机访问
while ( ! q . empty ()) {
//输出队头元素
cout << "队头元素-- 姓名: " << q . front (). m_Name
<< " 年龄: " << q . front (). m_Age << endl ;
cout << "队尾元素-- 姓名: " << q . back (). m_Name
<< " 年龄: " << q . back (). m_Age << endl ;
cout << endl ;
//弹出队头元素
q . pop ();
}
cout << "队列大小为:" << q . size () << endl ;
}
int main () {
test01 ();
system ( "pause" );
return 0 ;
}
总结:
入队 --- push
出队 --- pop
返回队头元素 --- front
返回队尾元素 --- back
判断队是否为空 --- empty
返回队列大小 --- size
4.8 list容器
4.8.1 list基本概念
功能: 将数据进行链式存储
链表 (list)是一种物理存储单元上非连续的存储结构,数据元素的逻辑顺序是通过链表中的指针链接实现的
链表的组成:链表由一系列结点 组成
结点的组成:一个是存储数据元素的数据域 ,另一个是存储下一个结点地址的指针域
STL中的链表是一个双向循环链表
list的优点:
采用动态存储分配,不会造成内存浪费和溢出
链表执行插入和删除操作十分方便,修改指针即可,不需要移动大量元素
list的缺点:
链表灵活,但是空间(指针域) 和 时间(遍历)额外耗费较大
List有一个重要的性质,插入操作和删除操作都不会造成原有list迭代器的失效,这在vector是不成立的。
总结:STL中List和vector是两个最常被使用的容器 ,各有优缺点
4.8.2 list构造函数
功能描述:
函数原型:
list<T> lst;
//list采用采用模板类实现,对象的默认构造形式:
list(beg,end);
//构造函数将[beg, end)区间中的元素拷贝给本身。
list(n,elem);
//构造函数将n个elem拷贝给本身。
list(const list &lst);
//拷贝构造函数。
示例:
C++ 1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38 #include <list>
void printList ( const list < int >& L ) {
for ( list < int >:: const_iterator it = L . begin (); it != L . end (); it ++ ) {
cout << * it << " " ;
}
cout << endl ;
}
void test01 ()
{
list < int > L1 ;
L1 . push_back ( 10 );
L1 . push_back ( 20 );
L1 . push_back ( 30 );
L1 . push_back ( 40 );
printList ( L1 );
list < int > L2 ( L1 . begin (), L1 . end ());
printList ( L2 );
list < int > L3 ( L2 );
printList ( L3 );
list < int > L4 ( 10 , 1000 );
printList ( L4 );
}
int main () {
test01 ();
system ( "pause" );
return 0 ;
}
总结:list构造方式同其他几个STL常用容器,熟练掌握即可
4.8.3 list赋值和交换
功能描述:
函数原型:
assign(beg, end);
//将[beg, end)区间中的数据拷贝赋值给本身。
assign(n, elem);
//将n个elem拷贝赋值给本身。
list& operator=(const list &lst);
//重载等号操作符
swap(lst);
//将lst与本身的元素互换。
示例:
C++ 1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72 #include <list>
void printList ( const list < int >& L ) {
for ( list < int >:: const_iterator it = L . begin (); it != L . end (); it ++ ) {
cout << * it << " " ;
}
cout << endl ;
}
//赋值和交换
void test01 ()
{
list < int > L1 ;
L1 . push_back ( 10 );
L1 . push_back ( 20 );
L1 . push_back ( 30 );
L1 . push_back ( 40 );
printList ( L1 );
//赋值
list < int > L2 ;
L2 = L1 ;
printList ( L2 );
list < int > L3 ;
L3 . assign ( L2 . begin (), L2 . end ());
printList ( L3 );
list < int > L4 ;
L4 . assign ( 10 , 100 );
printList ( L4 );
}
//交换
void test02 ()
{
list < int > L1 ;
L1 . push_back ( 10 );
L1 . push_back ( 20 );
L1 . push_back ( 30 );
L1 . push_back ( 40 );
list < int > L2 ;
L2 . assign ( 10 , 100 );
cout << "交换前: " << endl ;
printList ( L1 );
printList ( L2 );
cout << endl ;
L1 . swap ( L2 );
cout << "交换后: " << endl ;
printList ( L1 );
printList ( L2 );
}
int main () {
//test01();
test02 ();
system ( "pause" );
return 0 ;
}
总结:list赋值和交换操作能够灵活运用即可
4.8.4 list 大小操作
功能描述:
函数原型:
//如果容器变短,则末尾超出容器长度的元素被删除。
resize(num, elem);
//重新指定容器的长度为num,若容器变长,则以elem值填充新位置。Text Only //如果容器变短,则末尾超出容器长度的元素被删除。
示例:
C++ 1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45 #include <list>
void printList ( const list < int >& L ) {
for ( list < int >:: const_iterator it = L . begin (); it != L . end (); it ++ ) {
cout << * it << " " ;
}
cout << endl ;
}
//大小操作
void test01 ()
{
list < int > L1 ;
L1 . push_back ( 10 );
L1 . push_back ( 20 );
L1 . push_back ( 30 );
L1 . push_back ( 40 );
if ( L1 . empty ())
{
cout << "L1为空" << endl ;
}
else
{
cout << "L1不为空" << endl ;
cout << "L1的大小为: " << L1 . size () << endl ;
}
//重新指定大小
L1 . resize ( 10 );
printList ( L1 );
L1 . resize ( 2 );
printList ( L1 );
}
int main () {
test01 ();
system ( "pause" );
return 0 ;
}
总结:
判断是否为空 --- empty
返回元素个数 --- size
重新指定个数 --- resize
4.8.5 list 插入和删除
功能描述:
函数原型:
push_back(elem);//在容器尾部加入一个元素
pop_back();//删除容器中最后一个元素
push_front(elem);//在容器开头插入一个元素
pop_front();//从容器开头移除第一个元素
insert(pos,elem);//在pos位置插elem元素的拷贝,返回新数据的位置。
insert(pos,n,elem);//在pos位置插入n个elem数据,无返回值。
insert(pos,beg,end);//在pos位置插入[beg,end)区间的数据,无返回值。
clear();//移除容器的所有数据
erase(beg,end);//删除[beg,end)区间的数据,返回下一个数据的位置。
erase(pos);//删除pos位置的数据,返回下一个数据的位置。
remove(elem);//删除容器中所有与elem值匹配的元素。
示例:
C++ 1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64 #include <list>
void printList ( const list < int >& L ) {
for ( list < int >:: const_iterator it = L . begin (); it != L . end (); it ++ ) {
cout << * it << " " ;
}
cout << endl ;
}
//插入和删除
void test01 ()
{
list < int > L ;
//尾插
L . push_back ( 10 );
L . push_back ( 20 );
L . push_back ( 30 );
//头插
L . push_front ( 100 );
L . push_front ( 200 );
L . push_front ( 300 );
printList ( L );
//尾删
L . pop_back ();
printList ( L );
//头删
L . pop_front ();
printList ( L );
//插入
list < int >:: iterator it = L . begin ();
L . insert ( ++ it , 1000 );
printList ( L );
//删除
it = L . begin ();
L . erase ( ++ it );
printList ( L );
//移除
L . push_back ( 10000 );
L . push_back ( 10000 );
L . push_back ( 10000 );
printList ( L );
L . remove ( 10000 );
printList ( L );
//清空
L . clear ();
printList ( L );
}
int main () {
test01 ();
system ( "pause" );
return 0 ;
}
总结:
尾插 --- push_back
尾删 --- pop_back
头插 --- push_front
头删 --- pop_front
插入 --- insert
删除 --- erase
移除 --- remove
清空 --- clear
4.8.6 list 数据存取
功能描述:
函数原型:
front();
//返回第一个元素。
back();
//返回最后一个元素。
示例:
C++ 1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30 #include <list>
//数据存取
void test01 ()
{
list < int > L1 ;
L1 . push_back ( 10 );
L1 . push_back ( 20 );
L1 . push_back ( 30 );
L1 . push_back ( 40 );
//cout << L1.at(0) << endl;//错误 不支持at访问数据
//cout << L1[0] << endl; //错误 不支持[]方式访问数据
cout << "第一个元素为: " << L1 . front () << endl ;
cout << "最后一个元素为: " << L1 . back () << endl ;
//list容器的迭代器是双向迭代器,不支持随机访问
list < int >:: iterator it = L1 . begin ();
//it = it + 1;//错误,不可以跳跃访问,即使是+1
}
int main () {
test01 ();
system ( "pause" );
return 0 ;
}
总结:
list容器中不可以通过[]或者at方式访问数据
返回第一个元素 --- front
返回最后一个元素 --- back
4.8.7 list 反转和排序
功能描述:
函数原型:
reverse();
//反转链表
sort();
//链表排序
示例:
C++ 1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43 void printList ( const list < int >& L ) {
for ( list < int >:: const_iterator it = L . begin (); it != L . end (); it ++ ) {
cout << * it << " " ;
}
cout << endl ;
}
bool myCompare ( int val1 , int val2 )
{
return val1 > val2 ;
}
//反转和排序
void test01 ()
{
list < int > L ;
L . push_back ( 90 );
L . push_back ( 30 );
L . push_back ( 20 );
L . push_back ( 70 );
printList ( L );
//反转容器的元素
L . reverse ();
printList ( L );
//排序
L . sort (); //默认的排序规则 从小到大
printList ( L );
L . sort ( myCompare ); //指定规则,从大到小
printList ( L );
}
int main () {
test01 ();
system ( "pause" );
return 0 ;
}
总结:
反转 --- reverse
排序 --- sort (成员函数)
4.9 set/multiset 容器
4.9.1 set基本概念
简介:
本质:
set/multiset属于关联式容器 ,底层结构是用二叉树 实现
set和multiset区别 :
set不允许容器中有重复的元素
multiset允许容器中有重复的元素
4.9.2 set构造和赋值
功能描述:创建set容器以及赋值
构造:
set<T> st;
//默认构造函数:
set(const set &st);
//拷贝构造函数
赋值:
set& operator=(const set &st);
//重载等号操作符
示例:
C++ 1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40 #include <set>
void printSet ( set < int > & s )
{
for ( set < int >:: iterator it = s . begin (); it != s . end (); it ++ )
{
cout << * it << " " ;
}
cout << endl ;
}
//构造和赋值
void test01 ()
{
set < int > s1 ;
s1 . insert ( 10 );
s1 . insert ( 30 );
s1 . insert ( 20 );
s1 . insert ( 40 );
printSet ( s1 );
//拷贝构造
set < int > s2 ( s1 );
printSet ( s2 );
//赋值
set < int > s3 ;
s3 = s2 ;
printSet ( s3 );
}
int main () {
test01 ();
system ( "pause" );
return 0 ;
}
总结:
set容器插入数据时用insert
set容器插入数据的数据会自动排序
4.9.3 set大小和交换
功能描述:
函数原型:
size();
//返回容器中元素的数目
empty();
//判断容器是否为空
swap(st);
//交换两个集合容器
示例:
C++ 1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72 #include <set>
void printSet ( set < int > & s )
{
for ( set < int >:: iterator it = s . begin (); it != s . end (); it ++ )
{
cout << * it << " " ;
}
cout << endl ;
}
//大小
void test01 ()
{
set < int > s1 ;
s1 . insert ( 10 );
s1 . insert ( 30 );
s1 . insert ( 20 );
s1 . insert ( 40 );
if ( s1 . empty ())
{
cout << "s1为空" << endl ;
}
else
{
cout << "s1不为空" << endl ;
cout << "s1的大小为: " << s1 . size () << endl ;
}
}
//交换
void test02 ()
{
set < int > s1 ;
s1 . insert ( 10 );
s1 . insert ( 30 );
s1 . insert ( 20 );
s1 . insert ( 40 );
set < int > s2 ;
s2 . insert ( 100 );
s2 . insert ( 300 );
s2 . insert ( 200 );
s2 . insert ( 400 );
cout << "交换前" << endl ;
printSet ( s1 );
printSet ( s2 );
cout << endl ;
cout << "交换后" << endl ;
s1 . swap ( s2 );
printSet ( s1 );
printSet ( s2 );
}
int main () {
//test01();
test02 ();
system ( "pause" );
return 0 ;
}
总结:
统计大小 --- size
判断是否为空 --- empty
交换容器 --- swap
4.9.4 set插入和删除
功能描述:
函数原型:
insert(elem);
//在容器中插入元素。
clear();
//清除所有元素
erase(pos);
//删除pos迭代器所指的元素,返回下一个元素的迭代器。
erase(beg, end);
//删除区间[beg,end)的所有元素 ,返回下一个元素的迭代器。
erase(elem);
//删除容器中值为elem的元素。
示例:
C++ 1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43 #include <set>
void printSet ( set < int > & s )
{
for ( set < int >:: iterator it = s . begin (); it != s . end (); it ++ )
{
cout << * it << " " ;
}
cout << endl ;
}
//插入和删除
void test01 ()
{
set < int > s1 ;
//插入
s1 . insert ( 10 );
s1 . insert ( 30 );
s1 . insert ( 20 );
s1 . insert ( 40 );
printSet ( s1 );
//删除
s1 . erase ( s1 . begin ());
printSet ( s1 );
s1 . erase ( 30 );
printSet ( s1 );
//清空
//s1.erase(s1.begin(), s1.end());
s1 . clear ();
printSet ( s1 );
}
int main () {
test01 ();
system ( "pause" );
return 0 ;
}
总结:
插入 --- insert
删除 --- erase
清空 --- clear
4.9.5 set查找和统计
功能描述:
函数原型:
find(key);
//查找key是否存在,若存在,返回该键的元素的迭代器;若不存在,返回set.end();
count(key);
//统计key的元素个数
示例:
C++ 1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37 #include <set>
//查找和统计
void test01 ()
{
set < int > s1 ;
//插入
s1 . insert ( 10 );
s1 . insert ( 30 );
s1 . insert ( 20 );
s1 . insert ( 40 );
//查找
set < int >:: iterator pos = s1 . find ( 30 );
if ( pos != s1 . end ())
{
cout << "找到了元素 : " << * pos << endl ;
}
else
{
cout << "未找到元素" << endl ;
}
//统计
int num = s1 . count ( 30 );
cout << "num = " << num << endl ;
}
int main () {
test01 ();
system ( "pause" );
return 0 ;
}
总结:
查找 --- find (返回的是迭代器)
统计 --- count (对于set,结果为0或者1)
4.9.6 set和multiset区别
学习目标:
区别:
set不可以插入重复数据,而multiset可以
set插入数据的同时会返回插入结果,表示插入是否成功
multiset不会检测数据,因此可以插入重复数据
示例:
C++ 1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41 #include <set>
//set和multiset区别
void test01 ()
{
set < int > s ;
pair < set < int >:: iterator , bool > ret = s . insert ( 10 );
if ( ret . second ) {
cout << "第一次插入成功!" << endl ;
}
else {
cout << "第一次插入失败!" << endl ;
}
ret = s . insert ( 10 );
if ( ret . second ) {
cout << "第二次插入成功!" << endl ;
}
else {
cout << "第二次插入失败!" << endl ;
}
//multiset
multiset < int > ms ;
ms . insert ( 10 );
ms . insert ( 10 );
for ( multiset < int >:: iterator it = ms . begin (); it != ms . end (); it ++ ) {
cout << * it << " " ;
}
cout << endl ;
}
int main () {
test01 ();
system ( "pause" );
return 0 ;
}
总结:
如果不允许插入重复数据可以利用set
如果需要插入重复数据利用multiset
4.10 map/multimap容器
4.10.1 map基本概念
简介:
map中所有元素都是pair(数据类型,不是容器)
pair中第一个元素为key(键值),起到索引作用,第二个元素为value(实值)
所有元素都会根据元素的键值自动排序
本质:
map/multimap属于关联式容器 ,底层结构是用二叉树实现。
优点:
map和multimap区别 :
map不允许容器中有重复key值元素
multimap允许容器中有重复key值元素
4.10.2 map构造和赋值
功能描述:
函数原型:
构造:
map<T1, T2> mp;
//map默认构造函数:
map(const map &mp);
//拷贝构造函数
赋值:
map& operator=(const map &mp);
//重载等号操作符
示例:
C++ 1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35 #include <map>
void printMap ( map < int , int >& m )
{
for ( map < int , int >:: iterator it = m . begin (); it != m . end (); it ++ )
{
cout << "key = " << it -> first << " value = " << it -> second << endl ;
}
cout << endl ;
}
void test01 ()
{
map < int , int > m ; //默认构造
m . insert ( pair < int , int > ( 1 , 10 ));
m . insert ( pair < int , int > ( 2 , 20 ));
m . insert ( pair < int , int > ( 3 , 30 ));
printMap ( m );
map < int , int > m2 ( m ); //拷贝构造
printMap ( m2 );
map < int , int > m3 ;
m3 = m2 ; //赋值
printMap ( m3 );
}
int main () {
test01 ();
system ( "pause" );
return 0 ;
}
总结:map中所有元素都是成对出现,插入数据时候要使用对组
4.10.3 map大小和交换
功能描述:
函数原型:
size();
//返回容器中元素的数目
empty();
//判断容器是否为空
swap(st);
//交换两个集合容器
示例:
C++ 1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63 #include <map>
void printMap ( map < int , int >& m )
{
for ( map < int , int >:: iterator it = m . begin (); it != m . end (); it ++ )
{
cout << "key = " << it -> first << " value = " << it -> second << endl ;
}
cout << endl ;
}
void test01 ()
{
map < int , int > m ;
m . insert ( pair < int , int > ( 1 , 10 ));
m . insert ( pair < int , int > ( 2 , 20 ));
m . insert ( pair < int , int > ( 3 , 30 ));
if ( m . empty ())
{
cout << "m为空" << endl ;
}
else
{
cout << "m不为空" << endl ;
cout << "m的大小为: " << m . size () << endl ;
}
}
//交换
void test02 ()
{
map < int , int > m ;
m . insert ( pair < int , int > ( 1 , 10 ));
m . insert ( pair < int , int > ( 2 , 20 ));
m . insert ( pair < int , int > ( 3 , 30 ));
map < int , int > m2 ;
m2 . insert ( pair < int , int > ( 4 , 100 ));
m2 . insert ( pair < int , int > ( 5 , 200 ));
m2 . insert ( pair < int , int > ( 6 , 300 ));
cout << "交换前" << endl ;
printMap ( m );
printMap ( m2 );
cout << "交换后" << endl ;
m . swap ( m2 );
printMap ( m );
printMap ( m2 );
}
int main () {
test01 ();
test02 ();
system ( "pause" );
return 0 ;
}
总结:
统计大小 --- size
判断是否为空 --- empty
交换容器 --- swap
4.10.4 map插入和删除
功能描述:
函数原型:
insert(elem);
//在容器中插入元素。
clear();
//清除所有元素
erase(pos);
//删除pos迭代器所指的元素,返回下一个元素的迭代器。
erase(beg, end);
//删除区间[beg,end)的所有元素 ,返回下一个元素的迭代器。
erase(key);
//删除容器中值为key的元素。
示例:
C++ 1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46 #include <map>
void printMap ( map < int , int >& m )
{
for ( map < int , int >:: iterator it = m . begin (); it != m . end (); it ++ )
{
cout << "key = " << it -> first << " value = " << it -> second << endl ;
}
cout << endl ;
}
void test01 ()
{
//插入
map < int , int > m ;
//第一种插入方式
m . insert ( pair < int , int > ( 1 , 10 ));
//第二种插入方式
m . insert ( make_pair ( 2 , 20 ));
//第三种插入方式
m . insert ( map < int , int >:: value_type ( 3 , 30 ));
//第四种插入方式
m [ 4 ] = 40 ;
printMap ( m );
//删除
m . erase ( m . begin ());
printMap ( m );
m . erase ( 3 );
printMap ( m );
//清空
m . erase ( m . begin (), m . end ());
m . clear ();
printMap ( m );
}
int main () {
test01 ();
system ( "pause" );
return 0 ;
}
总结:
map插入方式很多,记住其一即可
插入 --- insert
删除 --- erase
清空 --- clear
4.10.5 map查找和统计
功能描述:
函数原型:
find(key);
//查找key是否存在,若存在,返回该键的元素的迭代器;若不存在,返回set.end();
count(key);
//统计key的元素个数
示例:
C++ 1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35 #include <map>
//查找和统计
void test01 ()
{
map < int , int > m ;
m . insert ( pair < int , int > ( 1 , 10 ));
m . insert ( pair < int , int > ( 2 , 20 ));
m . insert ( pair < int , int > ( 3 , 30 ));
//查找
map < int , int >:: iterator pos = m . find ( 3 );
if ( pos != m . end ())
{
cout << "找到了元素 key = " << ( * pos ). first << " value = " << ( * pos ). second << endl ;
}
else
{
cout << "未找到元素" << endl ;
}
//统计
int num = m . count ( 3 );
cout << "num = " << num << endl ;
}
int main () {
test01 ();
system ( "pause" );
return 0 ;
}
总结:
查找 --- find (返回的是迭代器)
统计 --- count (对于map,结果为0或者1)