相遇皆是缘分

从C到C++

动态内存分配(new)

1
2
3
4
5
6
7
8
9
10
//用“new”动态分配的内存空间,一定要用“delete”运算符进行释放,否则无法释放,delete会调用析构函数

int *p=new int;
*p=5;
delete p;


int *p=new int[20];
p[0]=1;
delete [] p;

内联函数(了解)

1
2
3
4
5
6
7
8
9
// 在函数定义前面加 “inline” 关键字,即可定义内联函数

用于执行只有几条语句的函数,执行非常快,且经常被执行时,更能减少函数调用的开销

inline int Max(int a ,int b)
{
if(a>b) return a;
return b;
}

函数重载

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
// 一个或多个函数,名字相同,然后参数个数或参数类型不相同,这叫做函数的重载

int Max(double fl,double f2){}
int Max(int n1,int n2){}
int Max(int n1,int n2,int n3){}


【函数的缺省参数】
//让最右边的连续若干个参数有缺省值,那么调用函数的时候,相应的位置不写参数,参数就是缺省值
void func(int x1, int x2=2, int x3=3){}

func(10); //等效于 func(10,2,3)
func(10,8) //等效于 func(10,8,3)
func(10,,8) //不行,只能最右边的连续若干个参数缺省


类和对象

访问权限

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
/*
private: 私有成员,只能在成员函数内访问,可以通过 set , get 来更改
public: 公有成员,可以在任何地方访问
protected: 保护成员
*/

A
class Person {
private://私有的(在主函数中,不可以使用)
// protected 受保护的(在主函数中,不可以使用)
float money;
public: //共有的
void set_money() { //set方法(函数)
this->money = 3.14f;
}
float get_money() { //get方法(函数)
return this->money;
}
};

构造函数和析构函数

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
构造函数
/*
1.名字与类名相同,可以有参数,不能有返回值
2.作用是:对对象进行初始化,如给成员变量赋初值
3.没写构造函数,编译器会生成一个默认无参数构造参数(不做任何操作)
4.自动运行,不需要自己调用
【创建对象时,构造函数一定存在,无论是你写的,还是默认生成的】
*/

【构造函数无参】
class Stu {
public:
const char *name;
const char *name2;
const char *name3;
const char *name4;
const char *name5;
Stu() { // “构造函数” 这个函数很特殊,没有返回值的类型,必须与类的名字相同
// 自动运行,不需要自己调用
// 常用来解决初始化问题
this->name = NULL;
this->name2 = NULL;
this->name3 = NULL;
this->name4 = NULL;
this->name5 = NULL;
cout << "我是构造函数\n";
}
~Stu() {// “析构函数” 这个函数很特殊,没有返回值的类型,在类的名字前加 ~
// 自动运行,不需要自己调用
// 常用来解决函数中资源进行释放
// 跟构造函数不同,只能有一个
cout << "我是析构函数\n";
}

};

int main(){
class Stu boy; //1.创建对象
return 0;
}
---------------------------------------------------------
【析构函数执行多少次?】
class A {
public:
A() {};
~A() {
cout << "析构函数\n";
};
};
int main() {

A* p = new A[2];
A* p2 = new A; // new 需要 用 delete 来释放执行析构函数,没有 delete p2
A a; //执行1次
delete[] p; //执行2次
return 0;
}
// 析构函数执行共3次
---------------------------------------------------------

【构造函数有参】
class Stu {

public:
int v;
Stu(int n) {
v=n;
}
};

int main(){
class Stu boy(3); //正确
class Stu boy(); //错误,没传参
return 0;
}
---------------------------------------------------------

【多个构造函数有参】

class Stu {
public:
int age;
int score;
Stu() {
cout << "我是构造函数Stu()\n";
}

Stu(int age) {
cout << "我是构造函数Stu1()\n";
}

Stu(int age,int score) {
cout << "我是构造函数Stu2()\n";
}
};
int main() {
class Stu boy;
// class Stu boy2(100);
// class Stu boy3(100,200);
return 0;
}


---------------------------------------------------------

复制构造函数

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
    
/*
定义:
1.只有一个参数,即对同类对象的引用
2.没有定义复制构造函数,会默认生成
3.形如 XXX(const XXX &) 或者 XXX( XXX& )

作用:
1.当用一个对象去初始化同类的另一个对象时

stu c1;
stu c2(c1);
stu c2=c1; //初始化语句,非赋值语句
上面二条语句等价

注意:对象间赋值不导致复制构造函数被调用

stu c1,c2;
c2=c1; //对象间赋值


2.如果某函数有一个函数是类A的对象,那么该函数被调用时,类A的复制构造函数将被调用

class A
{
public:
A(){};
A(A&a){
cout<<"复制构造函数"<<endl;
}
}
void Func(A a1){}
int main(){
A a2;
Func(a2);
return 0;
}

3.如果函数的返回值是类A的对象时,则函数返回时,A的复制构造函数被调用

class A
{
public:
int v;
A(int n){v=n;}
A(const A & a){
v=a.v;
cout<<"复制构造函数"<<endl;
}
}
A Func(){
A b(4);
return b;
}
int main(){
cout<<Func().v<<endl;
return 0;
}

*/

【默认复制构造函数】
class Stu {
private:
double real,imag;
}
Stu c1; //调用缺省无参构造函数
Stu c2(c1) //调用缺省的复制构造函数,将c2初始化成和c1一样

---------------------------------------------------------

【定义复制构造函数】

class Stu {
private:
double real,imag;
stu(){}
stu(const stu & c){
real = c.real;
imag = c.imag;
}
}
Stu c1; //调用定义无参构造函数
Stu c2(c1) //调用定义的复制构造函数,将c2初始化成和c1一样

类型转换构造函数

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
/*
1.定义转换构造函数的目的是实现类型的自动转换
2.只有一个参数,而且不是复制构造函数的构造函数,一般就可以看作是转换构造函数
3.当需要的时候,编译系统会自动调用转换构造函数,建立一个无名的临时对象。
*/
class Stu {
public:
double real,imag;
Stu(int i){ //类型转换构造函数
cout<<"类型转换构造函数"<<endl;
real = i; imag =0;
}
stu(double r ,double i){
real = r;
imag = i;
}
}

Stu c1(7,8);
Stu c2=12; //初始化语句,非赋值语句
c2=12; //12被自动转换成一个临时Stu对象,12现在是一个对象,不是值,12这个对象由类型转换构造函数初始化,初始化的值是real=i,img =0 ; 将初始化的值都赋值给 c2 , 现在c2的值为 real=12,img =0


类和对象提高

静态成员变量

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
1.普通成员变量每个对象有各自的一份,而静态成员变量一共就一份,为所有对象共享,全局。
// sizeof 运算符不会计算静态成员变量
2.普通成员函数必须具体作用于某个对象,而静态成员函数并不具体作用与某个对象。
3.因此静态成员不需要通过对象就能访。

静态成员:在说明前面加了static关键字的成员
static int nTotalArea; //静态成员变量
static void PrintTotal() //静态成员函数

//必须在定义类的文件中对静态成员变量进行一次说明或初始化
class Box{
public:
static int obj;
...
}
int Box::obj = 0; //初始化类 Box 的静态成员

//在静态成员函数中,不能访问非静态成员变量,也不能调用非静态成员函数
class Box{
public:
static int obj;
...
static int getCount()
{
return obj;
}
}
int Box::obj = 0; //初始化类 Box 的静态成员
int main(){
cout<<Box::getCount()<<endl; //静态成员函数即使在类对象不存在的情况下也能被调用
}

成员对象和封装类

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
class A {
public:
A() {
cout << this << "父类A的 无参数构造函数被调用\n";
}
A(int temp) { //构造函数重载
cout << this << "父类A的 有参数构造函数被调用\n";
}
};
class B : public A {
public:

B() {
cout << this << "父类B的 无参数构造函数被调用\n";
}

B(int temp) {
cout << this << "父类B的 有参数构造函数被调用\n";
}
};
int main(int argc, char** argv) {
class B obj_1;
cout << "-------------\n";
class B obj_2(11);
return 0;
}
/*
无参构造函数是默认调用有
调用子类B时,会自动调用父类A的无参构造函数,但如果你要求调用父类A的有参构造函数,需要用初始化列表,否则调用不了

为什么父类A中的构造函数被调用呢?
答:如果父类有Private修饰的成员变量,在子类中是无法使用,
也就是意味子类不能够对这些private成员变量进行初始化,因此就需要调用父类的构造函数
*/

初始化列表

初始化列表

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
【初始化列表调用父类的构造函数】
class A {
public:

A(int temp) { //构造函数重载
cout << this << "父类A的 有参数构造函数被调用\n";
}
};



class B : public A {
public:

B(): A(11) {
cout << this << "子类B的 无参数构造函数被调用\n";
}

//初始化列表,父类没有无参构造体,可以用有参构造体

B(int temp) : A(22) {
cout << this << "子类B的 有参数构造函数被调用\n";
}
};

int main(int argc, char** argv) {
class B obj_1;
cout << "-------------\n";
class B obj_2(11);
return 0;
}

-----------------------------------------------------

【初始化列表-多继承】
class A {
public:

A(int num) {
cout << "A::A(int num),num=" << num << ",this=" <<this<< "\n";
}
};

class B {
public:

B(int num) {
cout << "B::B(int num),num=" << num << ",this=" << this << "\n";
}
};

// 多继承初始化列表,按继承顺序调用,
// public A,public B 这里先调用父类A的,再调用父类B的
// public B,public A 这里先调用父类B的,再调用父类A的
class C : public A,public B {
public:

C() : B(200),A(100) {
cout << "C::C(),this=" << this << "\n";
}
C(int num) : A(num) , B(num + num) {
cout << "C::C(int num)\n";
}
};

int main() {
class C boy;
cout << "---------------------\n";
class C girl;

return 0;
}

常量对象 和 常量成员函数

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
【常量对象】
class Stu{
...
}
const Stu s; //常量对象

【常量成员函数】
1.在类的成员函数后面加 const ,是常量成员函数
2.常用对象执行常量成员函数可行,常量对象不能执行非常量成员函数

class Stu{
public:
void Sa() const {};
void func(){};
...
}
void Stu::Sa() const{
a=0;
fun();
}
int main{

const Stu o;
o.a=100; //不可被修改
o.fun(); //常量对象不能执行非常量成员函数
o.Sa(); //ok
return 0;

}

【注意】
两个成员函数,名字和参数表都一样,但是一个是const,一个不是,算重载。
class Stu{
public:
void Sa() const {...};
void Sa(){...};
...
}

友元

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
// friend :允许使用 private(私有)的变量或函数

class Car {
private:
int num;
public:
friend class SSSS; //声明 SSSS 为Car类里的友元类,让其可以使用,同类Car类里面的 private 成员
Car(int num) {
this->num = num; //类函数本身就可以使用类中private
}
void display_num() {
cout << "num=" << this->num << "\n";
}
};

class SSSS {
public:
void modify_car(Car* p) { //改装汽车
p->num = 720; //友元起作用
}
};

int main() {
class SSSS s;
class Car bmw(525);

bmw.display_num();
s.modify_car(&bmw);
bmw.display_num();

return 0;

}

运算符重载

成员函数方式

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
定义: operator 运算符符号

class Student{
public:
int age;
Student(int age):age(age){

}
Student operator+(Student &s){ //重载+
Student temp(this->age + s.age);
return temp;
}

};
int main(){
Student s1(10);
Student s2(20);
Student s3 = s1 + s2;
return 0;
}

全局函数方式

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
定义: operator 运算符符号

class Student{
public:
int age;
Student(int age):age(age){

}

};

Student operator+(Student &s,Student &ss){ //重载+ 全局函数
Student temp(s.age + ss.age);
return temp;
}

int main(){
Student s1(10);
Student s2(20);
Student s3 = s1 + s2;
return 0;
}

赋值运算符重载

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
class Distance
{
private:
int feet; // 0 到无穷
int inches; // 0 到 12
public:
// 所需的构造函数
Distance(){
feet = 0;
inches = 0;
}
Distance(int f, int i){
feet = f;
inches = i;
}
void operator=(const Distance &D )
{
feet = D.feet;
inches = D.inches;
}
// 显示距离的方法
void displayDistance()
{
cout << "F: " << feet << " I:" << inches << endl;
}

};
int main()
{
Distance D1(11, 10), D2(5, 11);

cout << "First Distance : ";
D1.displayDistance();
cout << "Second Distance :";
D2.displayDistance();

// 使用赋值运算符 , 效果类似拷贝构造
D1 = D2;
cout << "First Distance :";
D1.displayDistance();

return 0;
}

继承

继承和派生

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
107
108
109
110
111
112
113
114
115
116
117
118
//写法   class 派生类名 : public 基类名
子类 父类

//父类
class People {

private:
const char *name;
int age;

public:
void setName(const char *name) {
this->name = name;
}
const char *getName() {
return this->name;
}

void setAge(int age) {
this->age = age;
}
int getAge() {
return this->age;
}


};

//子类,继承
class Studnet : public People {

private:
float score;

public:
void setScore(float score) {
this->score = score;
}
float getScore() {
return this->score;
}
};

//子类,继承
class Staff : public People {

private:
float money;

public:
void setMoney(float money) {
this->money = money;
}
float getMoney() {
return this->money;
}


};

int main() {

//创建Studnet学生对象
class Studnet boy;
boy.setName("小明");
boy.setAge(16);
boy.setScore(95.5f);
cout << boy.getName() << "的年龄是" << boy.getAge() << ",成绩是" << boy.getScore() << "\n";

//创建Studnet员工对象
class Staff girl;
girl.setName("小丽");
girl.setAge(16);
girl.setMoney(4500.67);
cout << girl.getName() << "的年龄是" << girl.getAge() << ",成绩是" << girl.getMoney() << "\n";

return 0;
}


-----------------------------------------------------------------------------

【子类函数中使用父类函数】

class A {

private:

public:
int num;

void test() {
cout << "A类中的test()\n";
}

};

class B : public A {

public:
int num_2;
void test_2() {
cout << "B类中的test_2()\n";
this->num_2;
//如果想要使用父类中的被继承的成员(变量、函数),那么只需要在子类中,使用this->xxxx就可以
this->num;
this->test();
}
};

int main() {

class B b;
b.test_2();//创建了子类对象后,可以通过子类对象直接,调用子类中的成员
b.test(); //也可以直接调用继承的父类中的成员
return 0;
}

重写

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
class A {
public:
void test() {
cout << "A类中的test()\n";
}
void test(int num){
cout << "A类中的test(int num)\n";
}
};

// 重写:子类定义与父类相同名字的函数,覆盖了父类的这个函数

class B : public A {
public:
void test() { // 子类将父类相同名字的函数都覆盖了(包括 void test(int num) )
cout << "B类中的test()\n";
}

void test(int num) {
cout << "B类中的test(int num)\n";
}

void test(int num, int num2) {
cout << "B类中的test(int num, int num2)\n";
}

};

int main() {
class B boy;
boy.test();
boy.test(100);
boy.test(100,200);
return 0;

}

多层继承

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
class Grandpa {
public:
void display() {
cout << "Grandpa::display()" << "\n";
}

void display_3() {
cout << "Grandpa::display_3()" << "\n";
}
};

class Father : public Grandpa {
public:

void display() {
cout << "Father::display()" << "\n";
}

void display_2() {
cout << "Father::display_2()" << "\n";
}
};

class Children : public Father {
public:
void display() {
cout << "Children::display()" << "\n";

//可以调用被重写的父类的成员函数,使用父类的名字::函数名();

//调用父类的成员函数
this->display_2();
//调用被重写的父类成员函数(调用父类中的display()函数)
Father::display();
//调用爷爷类中的成员函数
this->display_3();
//调用被重写的爷爷类中成员函数(调用爷爷类中的display()函数)
Grandpa::display();
}
};

int main() {

class Children boy;
boy.display();


return 0;
}

多继承

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
class Father {
public:
void make_money() {
cout << "Father类的 make_money()\n";
}
};

class Mother {
public:
void make_homework() {
cout << "Mother类的 make_homework()\n";
}
void make_money() {
cout << "Mother类的 make_money()\n";
}
};

//多继承,一个子类继承多个父类
class Son : public Father, public Mother {
public:
void make_money() {
Father::make_money();
Mother::make_money();
}

};

int main() {

class Son s;

s.make_money();
s.make_homework();

return 0;
}

多态

多态实现的关键—虚函数表

虚函数

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
前面有 virtual 关键字的成员函数就是 虚函数

class Father {
public:
virtual void show() { // virtual,成为一个特殊的函数
cout << "father show" << endl;
}
};

class Children : public Father {
public:
virtual void show() {
cout << "children show" << endl;
}
};

int main() {

class Father *father = new Father();
father->show(); //调用父类的show函数

class Children *children = new Children();
children->show(); //调用子类的show函数

class Father *p = new Children();
p->show(); //通过virtual修饰后,输出右侧(输出指针p指向的)

return 0;
}

虚函数表

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
1.多态的函数调用语句被编译成一系列根据类指针所指向的对象中存放的虚函数表的地址,在虚函数表中查找虚函数地址并调用虚函数的指令
2.每一个有虚函数的类都有一个虚函数表,该类的任何对象中都放着虚函数表的指针。

class A{
public:
int i;
virtual void Print(){cout<<"A:Print";}
};
class B : public A{
public:
int n;
virtual void Print(){cout<<"B:Print";}
};
int main(){
B d;
cout<<sizeof(A)<<","<<sizeof(B);
return 0;
}
//输出:8,12 都多了4个字节,第一个虚函数的类都有一个虚函数表,4个字节装有虚函数表

虚析构函数

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

class A{
public:
~A(){cout<<"A"<<endl;}
};
class B : public A{
public:
~B(){cout<<"B"<<endl;}
};
int main(){
A *a;
a=new B();
delete a;
return 0;
}
//输出:A , 没有执行 子类析构函数

解决:给父类析构函数加了 virtual ,成为虚析构函数,子类也会默认成会 虚析构函数,不需要加 virtual

class A{
public:
virtual ~A(){cout<<"A"<<endl;}
};
class B : public A{
public:
~B(){cout<<"B"<<endl;}
};
int main(){
A *a;
a=new B();
delete a;
return 0;
}
//输出:B A

纯虚函数、抽象类

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
 包括 纯虚函数 的类叫 抽象类
抽象类 只能作为 基类(父类),不能创建独立的抽象类的对象(只有抽象类作为父类没有子类不能创建,有子类可以创建)
抽象类的指针和引用可以指向由抽象类派生出来的类的对象
在抽象类的成员函数内可以调用纯虚函数,但是在构造函数或析构函数内部不能调用纯虚函数
如果一个类从抽象类派生而来,那么当且仅当它实现了基类中的所有纯虚函数,它才能成为非抽象类

class Test { // 抽象类
public:
virtual void test() { // 虚函数

}

virtual void test2() = 0; //纯虚函数:只有函数名,没有具体过程
void g(){this->test2();} //多态,这里是调用的子类B的test2(),调用父类Test的test2()会出错
Test(){
this->test2(); //不是多态,调用父类Test的test2()会出错
}
};

//纯虚函数 用于子类,父类写一个大概的框架,不写具体过程,子类来重写补充具体过程

class B :public Test {
public:
void test2() {
cout << "hello\n";
}
};

int main() {

class Test t; //一个拥有纯虚函数的类,是不能创建对象的,那么这个类叫 : 抽象类

class B tt;
tt.test2();

return 0;
}

输入输出和模板

输入输出相关类

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
【标准流对象】
/*
cin 标准输入 用于从键盘读取数据,也可以被 重定向 为从文件中读取数据
cout 标准输出 用于向屏幕输出数据,也可以被 重定向 为向文件写入数据
cerr/clog 用于向屏幕输出出错信息
*/
重定向例子
【输出】
int main(){
int x,y;
cin>>x>>y;
freopen("test.txt","w",stdout); //将标准输出 重定向 到test.txt文件
if(y==0) //除数为0则在屏幕上输出错误信息
cerr<<"error."<<endl;
else
cout<<x/y; //输出结果到test.txt
return 0;
}

【输入】
int main(){
double f; int n;
freopen("test.txt","r",stdin); //将标准输入 cin被改为从test.txt中读取数据
cin>>f>>n;
cout<<f<<","<<n<<endl;
return 0;
}
---------------------------------------------------------------------

流操纵算子

头文件 #include

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
107
108
109
110
111
112
113
114
115
116
117
118
119
整数流的基数:流操纵算子 dec(十进制) , oct(八进制) , hex(十六进制) , setbase(任意进制)
浮点数的精度 (precision,setprecision)
设置域宽 (setw,width)
用户自定义的流操纵算子
--------------------------------------------
【整数流的基数-流操纵算子】 dec(十进制) , oct(八进制) , hex(十六进制)
int n =10;
cout <<n <<endl ;
cout <<hex <<n<<"\n"
<<dec <<n<<"\n"
<<oct <<n<<endl ;

输出结果:
10
a
10
12
---------------------------------------------
【浮点数的精度-流操纵算子】 precision,setprecision

precision是成员函数,其调用方式为:
cout.precision(5);

setprecision是流操作算子,其调用方式为:
cout<<setprecision(5);//可以连续输出

它们的功能相同
指定输出浮点数的有效位数(非定点方式输出时)
指定输出浮点数的小数点后的有效位数(定点方式输出时)
定点方式:小数点必须出现在个位数后面

int main()
{
double x=1234567.89;
// 有效数字6位,但x有9位,6 < 9 , 非定点方式输出(数学计数法),如果有效数字大于x的位数,则用定点方式输出
cout<< setprecision(6) << x << endl <<
// fixed 以小数点位置固定的方式输出,定点方式输出
fixed << x << endl <<
// scientific 取消以小数点位置固定的方式输出,非定点方式输出(数学计数法)
scientific << x << endl;
}

输出结果:
1.234457e+006
1234567.890000
1.234457e+006

--------------------------------------------------------------
【设置域宽】 Setw , width
两者功能相同,一个成员函数,另一个是流操作算子,调用方式不同:
cin>>setw(4); 或者 cin.width(5);
cout<setw(4); 或者 cout.width(5);

宽度设置有效性是一次性的,在每次读入和输出之前都要设置宽度

int w = 4;
char string[10];
cin.width(5);
while(cin>>string){
cout.width(w++);
cout<<stirng<<endl;
cin>.width(5);
}

输入:1234567890
输出:
1234 //输入的宽为5,包括了 \n , 输出的宽为5
5678 //输入的宽为5,包括了 \n , 输出的宽为6,5<6, 不够补1空格
90 //输入的宽为5,实际输入3,包括了 \n , 输出的宽为7,3<7, 不够补4空格

------------------------------------------------------------
【快速回顾】
#include <iostream>
#include <iomanip>
using namespace std;
int main()
{
int n = 141;
//1) 分别以十六进制、十进制、八进制先后输出 n
cout << "1) " << hex << n << " " << dec << n << " " << oct << n << endl; double x = 1234567.89, y = 12.34567;
//2) 保留5位有效数字
cout << "2) " << setprecision(5) << x << " " << y << " " << endl;
//3) 保留小数点后面5位
cout << "3) " << fixed << setprecision(5) << x << " " << y << endl;
//4) 科学计数法输出,且保留小数点后面5位
cout << "4) " << scientific << setprecision(5) << x << " " << y << endl;
//5) 非负数要显示正号,输出宽度为12字符,宽度不足则用'*'填补
cout << "5) " << showpos << fixed << setw(12) << setfill('*') << 12.1 << endl;
//6) 非负数不显示正号,输出宽度为12字符,宽度不足则右边用填充字符填充
cout << "6) " << noshowpos << setw(12) << left << 12.1 << endl;
//7) 输出宽度为12字符,宽度不足则左边用填充字符填充
cout << "7) " << setw(12) << right << 12.1 << endl;
//8) 宽度不足时,负号和数值分列左右,中间用填充字符填充
cout << "8) " << setw(12) << internal << -12.1 << endl;
cout << "9) " << 12.1 << endl;
return 0;
}

输出结果:
1) 8d 141 215
2) 1.2346e+06 12.346
3) 1234567.89000 12.34567
4) 1.23457e+06 1.23457e+01
5) ***+12.10000
6) 12.10000****
7) ****12.10000
8) -***12.10000
9) 12.10000


-------------------------------------------------
【用户自定义流操纵算子】
ostream &tab(ostream &output){
return output << '\t';
}
cout<<"aa"<<tab<<"bb"<<endl;

输出: aa bb

文件读写

头文件 #include

创建文件

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
方式一:
ofstream outFile("t.dat",ios::out|ios::binary);

ios::out 文件撕打开方式
ios::out 输出到文件,删除原有内容
ios::app 输出到文件,保留原有内容,总是在尾部添加
ios::ate (at end)文件打开后定位到文件末尾
ios::in 打开文件用于读取
ios::binary 以二进制文件格式打开文件
--------------------------------------------------------------
方式二:
也可以先创建ofstream对象,再用open函数打开
ofstream fout;
fout.open("test.out",ios::out|ios::binary);

判断打开是否成功:
if(!fout){
cout << "File open error!" << endl;
}

文件的读写指针

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
【写】
ofstream fout("a1.out",ios::app); //以添加方式打开
long location = fout.tellp(); //取得写指针的位置
location = 10; //location 可以为负值
fout.seekp(location); // 将写指针移动到第10个字节处
fout.seekp(location,ios::beg); //从头数location
fout.seekp(location,ios::cur); //从当前位置数
location fout.seekp(location,ios::end); //从尾部数location

--------------------------------------------------------
【读】
ifstream fin(“a1.in”,ios::ate);
//打开文件,定位文件指针到文件尾
long location = fin.tellg(); //取得读指针的位置
location = 10L; //location 可以为负值
fin.seekg(location); // 将读指针移动到第10个字节处
fin.seekg(location,ios::beg); //从头数location
fin.seekg(location,ios::cur); //从当前位置数location
fin.seekg(location,ios::end); //从尾部数location

字符/文本文件读写

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
写一个程序,将文件 in.txt 里面的整数排序后,输出到out.txt
例如,若in.txt 的内容为:
1 234 9 45 6 879
则执行本程序后,生成的out.txt的内容为:
1 6 9 45 234 879

int main() {
vector<int> v; //数组容器
ifstream srcFile("in.txt",ios::in); //以读取方式打开 in.txt
ofstream destFile("out.txt",ios::out); //以写入方式打开 out.txt
int x;
while( srcFile >> x )
v.push_back(x); //数组添加函数
sort(v.begin(),v.end());
for( int i = 0;i < v.size();i ++ )
destFile << v[i] << " ";
destFile.close(); //关闭 out.txt 文件
srcFile.close(); //关闭 in.txt 文件
return 0;
}

二进制文件读写

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
ifstream 和 fstream的成员函数
read(char* s,long n);

write(const char* s,long n);


在文件中写入和读取一个整数
int main() {
ofstream fout("some.dat", ios::out | ios::binary); //二进制流写入的方式打开
int x=120;
fout.write( (const char *)(&x), sizeof(int) ); //写入
fout.close();
ifstream fin("some.dat",ios::in | ios::binary); //二进制流读取的方式打开
int y;
fin.read((char * ) & y,sizeof(int)); //读取
fin.close();
cout << y <<endl;
return 0;
}

----------------------------------------------------
从键盘输入几个学生的姓名的成绩,并以二进制文件形式保存
struct Student {
char name[20];
int score;
};
int main() {
Student s;
ofstream OutFile( "c:\\tmp\\students.dat",ios::out|ios::binary); //二进制流写入的方式打开
while( cin >> s.name >> s.score )
OutFile.write( (char * ) & s, sizeof( s) ); //写入
OutFile.close();
return 0;
}

输入:
Tom 60
Jack 80
Jane 40
则形成的 students.dat 为 72字节
----------------------------------------------------
struct Student {
char name[20];
int score;
};
int main() {
Student s;
ifstream inFile("students.dat",ios::in | ios::binary );
if(!inFile) { //文件打开是否成功
cout << "error" <<endl;
return 0;
}
while( inFile.read( (char* ) & s, sizeof(s) ) ) { //读到文件尾
int readedBytes = inFile.gcount(); //看刚才读了多少字节
cout << s.name << " " << s.score << endl;
}
inFile.close(); return 0;
}

-----------------------------------------------------
将students.dat文件的Jane的名字改成Mike
Tom 60
Jack 80
Jane 40
则形成的 students.dat 为 72字节

struct Student {
char name[20];
int score;
};
int main(){
Student s;
fstream iofile( "c:\\tmp\\students.dat", ios::in|ios::out|ios::binary); //fstream 可读可写
if( !iofile) { //文件打开是否成功
cout << "error" ;
return 0;
}
iofile.seekp( 2 * sizeof(s),ios::beg); //定位写指针到第三个记录(Jane下标位置)
iofile.write("Mike",strlen("Mike")+1); //重写Mike,需要多一个位置\0
iofile.seekg(0,ios::beg); //定位读指针到开头
while( iofile.read( (char* ) & s, sizeof(s)) )
cout << s.name << " " << s.score << endl;
iofile.close();
return 0;

输出:
Tom 60
Jack 80
Mike 40

函数模板

1
2
3
4
5
6
7
8
9
10
11
12
13
template <class 类型参数1,class 类型参数2,...>
返回值类型 模板名(形参表)
{
函数体
};
---------------------------------------------
template<class T>
void Swap(T & X,T & y)
{
T tmp =x ;
x = y;
y = tmp;
}

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
【求数组最大元素】    通过参数实例化函数模板 
template<class T>
void MaxElement(T a[],int size) //size是数组元素个数
{
T tepMax = a[0];
for(int i =1;i<size;++i)
if(tmpMax < a[i])
tepMax = a[i];
return tmpMax;
}
int main(){

...
MaxElement(a[],10); // 通过参数实例化函数模板
}
-----------------------------------------------------
不通过参数实例化函数模板
template<class T>
T inc(T n)
{
return 1+n;
}
int main(){
cout<< inc<double>(4)/2; //不通过参数实例化函数模板 double代替T -> double inc(double n)
return 0;
}
//输出 2.5
----------------------------------------------------
函数模板可以重载,只要它们的 形参表 或 类型参数表 不同即可

template<class T1, class T2> //类型参数表
void print(T1 arg1, T2 arg2) { //形参表
cout<< arg1 << " "<< arg2<<endl;
}

template<class T> //类型参数表
void print(T arg1, T arg2) { //形参表
cout<< arg1 << " "<< arg2<<endl;
}

template<class T,class T2> //类型参数表
void print(T arg1, T arg2) { //形参表
cout<< arg1 << " "<< arg2<<endl;
}
----------------------------------------------------
有多个函数和函数模板名字相同的情况下
1) 先找参数完全匹配的普通函数(非由模板实例化而得的函数)。
2) 再找参数完全匹配的模板函数。
3) 再找实参数经过自动类型转换后能够匹配的普通函数。
4) 上面的都找不到,则报错。
----------------------------------------------------
函数模板示例:Map
将某个区间的数,通过某种方法后,用另一个数组写入

template<class T,class Pred>
void Map(T s, T e, T x, Pred op){ // s:区间起始位置 e:区间终止位置 x:数组起始位置 op:方法(函数)
for(; s != e; ++s,++x){
*x = op(*s);
}
}
int Cube(int x) { return x * x * x; } //方法函数:立方
double Square(double x) { return x * x; } //方法函数:平方
int a[5] = {1,2,3,4,5}, b[5];
double d[5] = { 1.1,2.1,3.1,4.1,5.1} , c[5];
int main() {
Map(a,a+5,b,Square);
for(int i=0;i < 5; ++i) cout << b[i] << ",";
cout << endl;
Map(a,a+5,b,Cube);
for(int i=0;i < 5; ++i) cout << b[i] << ",";
cout << endl;
Map(d,d+5,c,Square);
for(int i =0;i < 5; ++i) cout << c[i] << ",";
cout << endl;
return 0;
}

类模板

1
2
3
4
5
6
7
8
9
10
11
12
13
14
template <class 类型参数1,class 类型参数2,...>  //类型参数表
class 类模板名
{
成员函数和成员变量
};

//类模板成员函数的写法:
返回值类型 类模板名<类型参数名列表>::成员函数名(参数表)
{
...
}

//用类模板定义对象的写法:
类模板名<真实类型参数表> 对象名(构造函数实参表);
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
类模板示例: Pair类模板

template <class T1,class T2>
class Pair
{
public:
T1 key; //关键字
T2 value; //值
Pair(T1 k,T2 v):key(k),value(v) { };
bool operator < ( const Pair<T1,T2> & p) const;
};
template<class T1,class T2>
bool Pair<T1,T2>::operator < ( const Pair<T1,T2> & p) const
//Pair的成员函数 operator
{
return key < p.key;
}
int main(){
Pair<string,int> student("Tom",19);
//实例化出一个类 Pair<string,int>
cout << student.key << " " << student.value;
return 0;
}
----------------------------------------------------
函数模版作为类模板成员
#include <iostream>
using namespace std; template <class T>
class A
{
public:
template<class T2>
void Func( T2 t) { cout << t; } //成员函数模板
};
int main() {
A<int> a;
a.Func('K'); //成员函数模板 Func被实例化
a.Func("hello"); //成员函数模板 Func再次被实例化
return 0;
}
输出:KHello
-----------------------------------------------------
类模板与非类型参数
template <class T, int size>
class CArray{
T array[size];
public:
void Print( ) {
for( int i = 0;i < size; ++i)
cout << array[i] << endl;
}
};
CArray<double,40> a2;
CArray<int,50> a3;

类模板与派生/友元/静态成员变量

8_模板.pdf

标准模板库STL

String类

头文件

常见构造

函数名称功能说明
string()构造空的string类对象,即空字符串
string(const char* s)用C风格字符串string来构造string类对象
string(size_t n, char c)string类对象中包含n个字符c
string(const string&s)拷贝构造函数
string(const string&s, size_t n)用s中的前n个字符构造新的string类对象
1
2
3
4
5
6
7
void TestString(){
string s1; // 构造空的string类对象s1
string s2("giturtle"); // 用C格式字符串构造string类对象s2
string s3(10, 'a'); // 用10个字符'a'构造string类对象s3
string s4(s2); // 拷贝构造s4
string s5(s3, 5); // 用s3中第5个字符起、字符串结尾止的字符串构造string对象s5
}

容量操作

函数名称功能说明
size (重点)返回字符串有效字符长度
length返回字符串有效字符长度
empty (重点)检测字符串释放为空串,是返回true,否则返回****false
clear (重点)清空有效字符
reserve (重点)为字符串预留空间
resize (重点)将有效字符的个数该成 n 个,多出的空间用字符 c 填充
capacity返回空间总大小
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
// 测试string容量相关的接口
// size/clear/resize
void Teststring1()
{
// string类对象支持直接用cin和cout进行输入和输出
string s("hello, world");
cout << s.size() << endl;
cout << s.length() << endl;
cout << s.capacity() << endl;
cout << s << endl;

// 将s中的字符串清空,注意清空时只是将size清0,不改变底层空间的大小
s.clear();
cout << s.size() << endl;
cout << s.capacity() << endl;

// 将s中有效字符个数增加到10个,多出位置用'a'进行填充
// “aaaaaaaaaa”
s.resize(10, 'a');
cout << s.size() << endl;
cout << s.capacity() << endl;

// 将s中有效字符个数增加到15个,多出位置用缺省值'\0'进行填充
// "aaaaaaaaaa\0\0\0\0\0"
// 注意此时s中有效字符个数已经增加到15个
s.resize(15);
cout << s.size() << endl;
cout << s.capacity() << endl;
cout << s << endl;

// 将s中有效字符个数缩小到5个
s.resize(5);
cout << s.size() << endl;
cout << s.capacity() << endl;
cout << s << endl;
}

访问及遍历操作

函数名称功能说明
operator[ ] (重点)返回 pos 位置的字符, const string 类对象调用
begin + endbegin 获取一个字符的迭代器 + end 获取最后一个字符下一个位置的迭代器
rbegin + rendbegin 获取一个字符的迭代器 + end 获取最后一个字符下一个位置的迭代器****
范围 forC++11 支持更简洁的范围 for 的新遍历方式
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
// string的遍历
// begin()+end() for+[] 范围for
// 注意:string遍历时使用最多的还是for+下标 或者 范围for(C++11后才支持)
// begin()+end()大多数使用在需要使用STL提供的算法操作string时,比如:采用reverse逆置string
void Teststring()
{
string s("hello string");
// 3种遍历方式:
// 需要注意的以下三种方式除了遍历string对象,还可以遍历是修改string中的字符,
// 另外以下三种方式对于string而言,第一种使用最多
// 1. for+operator[]
for (size_t i = 0; i < s.size(); ++i)
cout << s[i] << endl;

// 2.迭代器
string::iterator it = s.begin();
while (it != s.end())
{
cout << *it << endl;
++it;
}

// string::reverse_iterator rit = s.rbegin();
// C++11之后,直接使用auto定义迭代器,让编译器推到迭代器的类型
auto rit = s.rbegin();
while (rit != s.rend())
cout << *rit << endl;

// 3.范围for
for (auto ch : s)
cout << ch << endl;
}

修改操作

函数名称功能说明
push_back在字符串后尾插字符 c
append在字符串后追加一个字符串
operator+= ( 重点 )在字符串后追加字符串 str
c_str ( 重点 )返回 C 格式字符串
find + npos ( 重点 )从字符串 pos 位置开始往后找字符 c ,返回该字符在字符串中的位置
rfind从字符串 pos 位置开始往前找字符 c ,返回该字符在字符串中的位置
substr str 中从 pos 位置开始,截取 n 个字符,然后将其返回
1
2
3
4
5
6
7
8
9
10
void Teststring5()
{
string str;
str.push_back(' '); // 在str后插入空格
str.append("hello"); // 在str后追加一个字符"hello"
str += 'wo'; // 在str后追加一个字符'wo'
str += "rld"; // 在str后追加一个字符串"rld"
cout << str << endl;
cout << str.c_str() << endl; // 以C语言的方式打印字符串
}

常用方法

[C++string类常用方法_c++ string类的常用方法-CSDN博客](https://blog.csdn.net/qq_40644809/article/details/108977918?ops_request_misc={"request_id"%3A"171297534816800178590165"%2C"scm"%3A"20140713.130102334.pc_all."}&request_id=171297534816800178590165&biz_id=0&utm_medium=distribute.pc_search_result.none-task-blog-2~all~first_rank_ecpm_v1~hot_rank-2-108977918-null-null.142^v100^pc_search_result_base3&utm_term=c%2B%2B string&spm=1018.2226.3001.4187)

[C++中的String的常用函数用法总结_string函数-CSDN博客](https://blog.csdn.net/qq_37941471/article/details/82107077?ops_request_misc={"request_id"%3A"171297534816800178590165"%2C"scm"%3A"20140713.130102334.pc_all."}&request_id=171297534816800178590165&biz_id=0&utm_medium=distribute.pc_search_result.none-task-blog-2~all~first_rank_ecpm_v1~hot_rank-11-82107077-null-null.142^v100^pc_search_result_base3&utm_term=c%2B%2B string&spm=1018.2226.3001.4187)

标准模板库STL概述

1
2
3
4
5
容器:可容纳各种数据类型的通用数据结构,是类模板
迭代器:可用于依次存取容器中元素,类似于指针
算法:用来操作容器中的元素的函数模板
sort()来对一个 vector 中的数据进行排序
find()来搜索一个list中的对象

容器

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
可以用于存放各种类型的数据(基本类型的变量,对象等)的数据结构,都是类模版,分为三种:
1)顺序容器
vector, deque,list
2)关联容器
set, multiset, map, multimap
3)容器适配器
stack, queue, priority_queue

-----------------------------------------------

//顺序容器
【vector】
头文件 <vector>
动态数组。元素在内存连续存放。随机存取任何元素都能在常数时间完成。在尾端增删元素具有较佳的性能(大部分情况下是常数时间)。

【deque】
头文件 <deque>
双向队列。元素在内存连续存放。随机存取任何元素都能在常数时间完成(但次于vector)。在两端增删元素具有较佳的性能(大部分情况下是常数时间)。

【list】
头文件 <list>
双向链表。元素在内存不连续存放。在任何位置增删元素都能在常数时间完成。不支持随机存取。

-----------------------------------------------

//关联容器
 元素是排序的
 插入任何元素,都按相应的排序规则来确定其位置
 在查找时具有非常好的性能
 通常以平衡二叉树方式实现,插入和检索的时间都是 O(log(N))

【set/multiset】
头文件 <set>
set 即集合。set中不允许相同元素,multiset 中允许存在相同的元素。

multiset(mou t)

【map/multimap】
头文件 <map>
map与set的不同在于map中存放的元素有且仅有两个成员变量,一个名为first,另一个名为second, map根据first值对元素进行从小到大排序,并可快速地根据first来检索元素。
map同multimap的不同在于是否允许相同first值的元素。

【stack】
头文件 <stack>
栈。是项的有限序列,并满足序列中被删除、检索和修改的项只能是最近插入序列的项(栈顶的项)。"后进先出"

出栈 入栈
↖ ↗
\ /
___________
top——> | an |
———————————
| ... |
———————————
| ... |
———————————
| ... |
———————————
bottom——> | a1 |
———————————

【queue】
头文件 <queue>
队列。插入只可以在尾部进行,删除、检索和修改只允许从头部进行。"先进先出"

入队
\

___________
rear——> | an |
———————————
| ... |
———————————
| ... |
———————————
| ... |
———————————
front——> | a1 |
———————————
\

出队


【priority_queue】
头文件 <queue>
优先级队列。最高优先级元素总是第一个出列

顺序容器和关联容器中都有的成员函数
begin返回指向容器中第一个元素的迭代器
end返回指向容器中最后一个元素后面的位置的迭代器
rbegin返回指向容器中最后一个元素的迭代器
rend返回指向容器中第一个元素前面的位置的迭代器
erase从容器中删除一个或几个元素
clear从容器中删除所有元素
顺序容器的常用成员函数
front返回容器中第一个元素的引用
back返回容器中最后一个元素的引用
push_back在容器末尾增加新元素
pop_back删除容器末尾的元素
erase删除迭代器指向的元素(可能会使该迭代器失效),或删除一个区间,返回被删除元素后面的那个元素的迭代器

迭代器

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
	用于指向顺序容器和关联容器中的元素
 迭代器用法和指针类似
 有const 和非 const两种
 通过迭代器可以读取它指向的元素
 通过非const迭代器还能修改其指向的元素

定义一个容器类的迭代器的方法可以是:
// 容器类名::iterator 变量名;
或:
// 容器类名::const_iterator 变量名;
访问一个迭代器指向的元素:
// * 迭代器变量名

iterator(一特端特)
-----------------------------------------------

int main() {
vector<int> v; //一个存放int元素的数组,一开始里面没有元素
v.push_back(1); v.push_back(2); v.push_back(3); v.push_back(4);
vector<int>::const_iterator i; //常量迭代器
for( i = v.begin();i != v.end();++i ) //(v.end() 指向数组v未尾数的后面的一个位置)
cout << * i << ",";
cout << endl;
vector<int>::reverse_iterator r; //反向迭代器
for( r = v.rbegin();r != v.rend();r++ ) //从后往前走
cout << * r << ",";
cout << endl;
vector<int>::iterator j; //非常量迭代器
for( j = v.begin();j != v.end();j ++ )
* j = 100;
for( i = v.begin();i != v.end();i++ )
cout << * i << ",";
}

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
'双向迭代器'
若p和p1都是双向迭代器,则可对p、p1可进行以下操作:
++p, p++ 使p指向容器中下一个元素
--p, p-- 使p指向容器中上一个元素
* p 取p指向的元素
p = p1 赋值
p == p1 , p!= p1 判断是否相等、不等

'随机访问迭代器'
若p和p1都是随机访问迭代器,则可对p、p1可进行以下操作:
 双向迭代器的所有操作
 p += i 将p向后移动i个元素
 p -= i 将p向向前移动i个元素
 p + i 值为: 指向 p 后面的第i个元素的迭代器
 p - i 值为: 指向 p 前面的第i个元素的迭代器
 p[i] 值为: p后面的第i个元素的引用
 p < p1, p <= p1, p > p1, p>= p1 //p < p1 : p所指向的元素在p1所指向的元素的前面
 p – p1 : p1和p之间的元素个数
部分算法(例如:sort , binary_search )
容器容器上的迭代器类别
vector随机访问
deque随机访问
list双向
set/multiset双向
map/multimap双向
stack不支持迭代器
queue不支持迭代器
priority_queue不支持迭代器
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
vector的迭代器是随机迭代器,
遍历 vector 可以有以下几种做法(deque亦然):
vector<int> v(100);
int i;
for(i = 0;i < v.size() ; i ++)
cout << v[i]; //根据下标随机访问
vector<int>::const_iterator ii;
for( ii = v.begin(); ii != v.end ();++ii)
cout << * ii;
for( ii = v.begin(); ii < v.end ();++ii )
cout << * ii;

//间隔一个输出:
ii = v.begin(); while( ii < v.end()) {
cout << * ii;
ii = ii + 2;
}
----------------------------------------------

list 的迭代器是双向迭代器,正确的遍历list的方法:
list<int> v;
list<int>::const_iterator ii;
for( ii = v.begin(); ii != v.end ();++ii )
cout << * ii;

错误的做法:
for( ii = v.begin(); ii < v.end ();++ii )
cout << * ii;

//双向迭代器不支持 <,list没有 [] 成员函数
for(int i = 0;i < v.size() ; i ++)
cout << v[i];

算法

头文件:

find()
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
template<class InIt, class T>
InIt find(InIt first, InIt last, const T& val);

first 和 last 这两个参数都是容器的迭代器
查找区间起点和终点[first,last) //first包含,last不包含
'find在[first,last)查找等于val的元素'
函数返回值是一个迭代器。如果找到,则该迭代器指向被找到的元素。如果找不到,则该迭代器等于last

--------------------------------------------------

int main() {
int array[10] = {10,20,30,40};
vector<int> v;
v.push_back(1); v.push_back(2);
v.push_back(3); v.push_back(4);
vector<int>::iterator p;
p = find(v.begin(),v.end(),3); //在容器里查找 3
if( p != v.end())
cout << * p << endl; //输出3
p = find(v.begin(),v.end(),9); //在容器里查找 9,不到等于p=last=4
if( p == v.end())
cout << "not found " << endl;
p = find(v.begin()+1,v.end()-2,1);
//整个容器:[1,2,3,4], 查找区间:[2,3)
if( p != v.end())
cout << * p << endl; //在容器里查找 9,不到等于p=last=3
int * pp = find( array,array+4,20);//数组名是迭代器
cout << * pp << endl;
}
相等
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
class A  {
int v;
public:
A(int n):v(n) { }
bool operator < ( const A & a2) const { //重载 < 运算符,
//必须为常量成员函数
cout << v << "<" << a2.v << "?" << endl;
return false;
}
bool operator ==(const A & a2) const {
cout << v << "==" << a2.v << "?" << endl; return v == a2.v;
}
};
int main()
{
A a [] = { A(1),A(2),A(3),A(4),A(5) };
cout << binary_search(a,a+4,A(9)); //折半查找
return 0;
}

输出结果:
3<9?
2<9?
1<9?
9<1?
1

vector / deque 和 list

vector

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
【vector】  
头文件 <vector>
动态数组。元素在内存连续存放。随机存取任何元素都能在常数时间完成。在尾端增删元素具有较佳的性能(大部分情况下是常数时间)。
------------------------------------------------------
void PrintVector( T s, T e){
for(; s != e; ++s)
cout << * s << " ";
cout << endl;
}
int main() {
int a[5] = { 1,2,3,4,5 };
vector<int> v(a,a+5); //将数组a的内容放入v
cout << "1) " << v.end() - v.begin() << endl;
//两个随机迭代器可以相减,输出 1) 5
cout <<"2) "; PrintVector(v.begin(),v.end());
//2) 1 2 3 4 5
v.insert(v.begin() + 2, 13); //在begin()+2位置插入 13
cout <<"3) "; PrintVector(v.begin(),v.end());
//3) 1 2 13 3 4 5
v.erase(v.begin() + 2); //删除位于 begin() + 2的元素
cout <<"4) "; PrintVector(v.begin(),v.end());
//4) 1 2 3 4 5
vector<int> v2(4,100); //v2 有4个元素,都是100
v2.insert(v2.begin(),v.begin()+ 1,v.begin()+3); //在v2插入v的一个区间
//将v的一段插入v2开头
cout << "5) v2: "; PrintVector(v2.begin(),v2.end());
//5) v2: 2 3 100 100 100
v.erase(v.begin() + 1, v.begin() + 3);
//删除 v 上的一个区间,即 2,3
cout << "6) "; PrintVector(v.begin(),v.end());
//6) 1 4 5
return 0;
}

------------------------------------------------------
【vector实现二维数组】
int main() {
vector<vector<int> > v(3);
//v有3个元素,每个元素都是vector<int> 容器
for(int i = 0;i < v.size(); ++i)
for(int j = 0; j < 4; ++j)
v[i].push_back(j);
for(int i = 0;i < v.size(); ++i) {
for(int j = 0; j < v[i].size(); ++j)
cout << v[i][j] << " ";
cout << endl;
}
return 0;
}
程序输出结果:
0 1 2 3
0 1 2 3
0 1 2 3

deque

1
2
3
4
5
6
【deque】
头文件 <deque>
双向队列。元素在内存连续存放。随机存取任何元素都能在常数时间完成(但次于vector)。在两端增删元素具有较佳的性能(大部分情况下是常数时间)。

所有适用于 vector的操作都适用于 deque。
deque还有 push_front(将元素插入到前面) 和pop_front(删除最前面的元素)操作,复杂度是O(1)

list

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
【list】
头文件 <list>
双向链表。元素在内存不连续存放。在任何位置增删元素都能在常数时间完成。不支持随机存取。
除了具有所有顺序容器都有的成员函数以外,还支持8个成员函数:
push_front: 在前面插入
pop_front: 删除前面的元素
sort: 排序 (list 不支持 STL 的算法 sort)
remove: 删除和指定值相等的所有元素
unique: 删除所有和前一个元素相同的元素(要做到元素不重复,则unique之前还需要 sort)
merge: 合并两个链表,并清空被合并的那个
reverse: 颠倒链表
splice: 在指定位置前面插入另一链表中的一个或多个元素,并在另一链表中删除被插入的元素

-------------------------------------------------
class A {
private:
int n;
public:
A( int n_ ) { n = n_; }
friend bool operator<( const A & a1, const A & a2);
friend bool operator==( const A & a1, const A & a2);
friend ostream & operator << (ostream & o, const A & a);
};
bool operator<( const A & a1, const A & a2) {
return a1.n < a2.n;
}
bool operator==( const A & a1, const A & a2) {
return a1.n == a2.n; }
ostream & operator <<(ostream & o, const A & a){
o<< a.n;
return o;
}
template <class T>
void PrintList(const list<T> & lst) {
//不推荐的写法,还是用两个迭代器作为参数更好
int tmp = lst.size();
if( tmp > 0 ) {
typename list<T>::const_iterator i;
i = lst.begin();
for( i = lst.begin();i != lst.end(); i ++)
cout << * i << ",";
}
}// typename用来说明 list<T>::const_iterator是个类型
//在vs中不写也可以
int main() {
list<A> lst1,lst2;
lst1.push_back(1);lst1.push_back(3);
lst1.push_back(2);lst1.push_back(4);
lst1.push_back(2);
lst2.push_back(10);lst2.push_front(20);
lst2.push_back(30);lst2.push_back(30);
lst2.push_back(30);lst2.push_front(40);
lst2.push_back(40);
cout << "1) "; PrintList( lst1); cout << endl;
// 1) 1,3,2,4,2,
cout << "2) "; PrintList( lst2); cout << endl;
// 2) 40,20,10,30,30,30,40,
lst2.sort();
cout<<"3) "; PrintList( lst2); cout<<endl;
//3) 10,20,30,30,30,40,40,
lst2.pop_front();
cout<<"4) "; PrintList( lst2); cout<<endl;
//4) 20,30,30,30,40,40,
lst1.remove(2); //删除所有和A(2)相等的元素
cout<<"5) "; PrintList( lst1); cout<<endl;
//5) 1,3,4,
lst2.unique(); //删除所有和前一个元素相等的元素
cout<<"6) "; PrintList( lst2); cout<<endl;
//6) 20,30,40,
lst2.push_back (100);lst2.push_back (200);
lst2.push_back (300);lst2.push_back (400);
list<A>::iterator p1,p2,p3;
p1 = find(lst1.begin(),lst1.end(),3);
p2 = find(lst2.begin(),lst2.end(),200);
p3 = find(lst2.begin(),lst2.end(),400);
lst1.splice(p1,lst2,p2, p3);
//将[p2,p3)插入p1之前,并从lst2中删除[p2,p3)
cout << "10) "; PrintList( lst1); cout << endl;
//10) 40,30,20,4,200,300,3,1,
cout << "11) "; PrintList( lst2); cout << endl;
//11) 100,400,
return 0;
}

函数对象

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
是个对象,但是用起来看上去像函数调用,实际上也执行了函数调用。
class CMyAverage {
public:
double operator()( int a1, int a2, int a3 ) {
//重载 () 运算符
return (double)(a1 + a2+a3) / 3;
}
};
CMyAverage average; //函数对象
cout << average(3,2,3); // average.operator()(3,2,3) 用起来看上去像函数调用 输出 2.66667


------------------------------------------------------

class MyLess
{
public:
bool operator() (int a1,int a2){ //个位数大的大
if( ( a1 % 10 ) < (a2%10) )
return true;
else
return false;
}
};
bool MyCompare(int a1,int a2){ //个位数大的小
if( ( a1 % 10 ) < (a2%10) )
return false;
else
return true;
}
int main(){
int a[] = {35,7,13,19,12};
cout << MyMax(a,5,MyLess()) << endl;
cout << MyMax(a,5,MyCompare) << endl;
return 0;
}
输出:
19
12


【写出 MyMax模板】
template <class T, class Pred>
T MyMax( T * p, int n, Pred myless)
{
T tmpmax = p[0];
for( int i = 1;i < n;i ++ )
if( myless(tmpmax,p[i]))
tmpmax = p[i];
return tmpmax;
};

关联容器

关联容器适合用于不断更新容器,不断查询容器

set和multiset

头文件

multiset的成员函数
iterator find(const T & val);在容器中查找值为val的元素,返回其迭代器。如果找不到,返回end()。
iterator insert(const T & val);将val插入到容器中并返回其迭代器。
void insert( iterator first,iterator last);将区间[first,last)插入容器。
int count(const T & val);统计有多少个元素的值和val相等。
iterator lower_bound(const T & val);查找一个最大的位置 it,使得[begin(),it) 中所有的元素都比 val 小。
iterator upper_bound(const T & val);查找一个最小的位置 it,使得[it,end()) 中所有的元素都比 val 大。
pair<iterator,iterator> equal_range(const T & val);同时求得lower_bound和upper_bound。
iterator erase(iterator it);删除it指向的元素,返回其后面的元素的迭代器(Visual studio 2010上如此,但是在C++标准和Dev 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

'multiset的用法

#include <set>
using namespace std; class A { };
int main() {
multiset<A> a; //就等价于 multiset<A, less<A>> a;
a.insert( A()); //错误
//插入元素时,multiset会将被插入元素和已有元素进行比较。
//由于less模板是用 < 进行比较的,所以,这都要求 A 的对象能用 < 比较,即适当重载了 <
//没有 < 运算符的重载函数
}

--------------------------------------------
//multiset用法示例
template <class T>
void Print(T first, T last)
{ for(;first != last ; ++first) cout << * first << " ";
cout << endl;
}
class A {
private:
int n;
public:
A(int n_ ) { n = n_; }
friend bool operator< ( const A & a1, const A & a2 ) { return a1.n < a2.n; }
friend ostream & operator<< ( ostream & o, const A & a2 ) { o << a2.n; return o; }
friend class MyLess;
};
struct MyLess {
bool operator()( const A & a1, const A & a2) //按个位数比大小
{ return ( a1.n % 10 ) < (a2.n % 10); }
};
typedef multiset<A> MSET1; //MSET1用 "<"比较大小
typedef multiset<A,MyLess> MSET2; //MSET2用 MyLess::operator()比较大小
int main()
{
const int SIZE = 6;
A a[SIZE] = { 4,22,19,8,33,40 };
MSET1 m1;
m1.insert(a,a+SIZE); //插入元素时,multiset会将被插入元素和已有元素进行比较。
m1.insert(22); //插入元素时,multiset会将被插入元素和已有元素进行比较。
cout << "1) " << m1.count(22) << endl; //输出 1) 2
cout << "2) "; Print(m1.begin(),m1.end()); //输出 2) 4 8 19 22 22 33 40
//m1元素:4 8 19 22 22 33 40
MSET1::iterator pp = m1.find(19);
if( pp != m1.end() ) //条件为真说明找到
cout << "found" << endl;
out << "3) "; cout << * m1.lower_bound(22) << "," //查找一个最大的位置 it,使得[begin(),it) 中所有的元素都比 val 小
// 4,8,19 [4,22) 在此区间内所有元素都比22小
<<* m1.upper_bound(22)<< endl; //查找一个最小的位置 it,使得[it,end()) 中所有的元素都比 val 大。
// 33 [33,40) 在些区间内所有元素都比22大
//输出 3) 22,33
pp = m1.erase(m1.lower_bound(22),m1.upper_bound(22)); // 22,22,33 [22,33)
//pp指向被删元素的下一个元素
cout << "4) "; Print(m1.begin(),m1.end()); //输出 4) 4 8 19 33 40 cout << "5) ";
cout << * pp << endl; //输出 5) 33
MSET2 m2; // m2里的元素按n的个位数从小到大排
m2.insert(a,a+SIZE);
cout << "6) "; Print(m2.begin(),m2.end()); //输出 6) 40 22 33 4 8 19
return 0;
}

--------------------------------------------
//set用法示例
set不会有重复元素

int main() {
typedef set<int>::iterator IT;
int a[5] = { 3,4,6,1,2 };
set<int> st(a,a+5); // st里是 1 2 3 4 6
pair< IT,bool> result;
result = st.insert(5); // st变成 1 2 3 4 5 6
if( result.second ) //插入成功则输出被插入元素
cout << * result.first << " inserted" << endl; //输出: 5 inserted
if( st.insert(5).second )
cout << * result.first << endl;
else
cout << * result.first << " already exists" << endl; //输出 5 already exists
pair<IT,IT> bounds =st.equal_range(4); //同时求得lower_bound和upper_bound。
cout << * bounds.first << "," << * bounds.second ; //输出:4,5
return 0;
}

map和multimap

头文件

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
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
map/multimap容器里放着的都是pair模版类的对象,且按first从小到大排序


 multimap中的元素由 <关键字,值>组成,每个元素是一个pair对象,关键字就是first成员变量,其类型是Key
 multimap中允许多个元素的关键字相同。元素按照first成员变量从小到大排列,缺省情况下用 less<Key> 定义关键字的“小于”关系。

//multimap用法示例
int main() {
// 成员变量 成员变量 比大小规则(用<比大小)
typedef multimap<int,double,less<int> > mmid;
mmid pairs;
cout << "1) " << pairs.count(15) << endl; //多少个关键字的值等于15
pairs.insert(mmid::value_type(15,2.7)); //typedef pair<const Key, T> value_type;
pairs.insert(mmid::value_type(15,99.3));
cout << “2) ” << pairs.count(15) << endl; //求关键字等于某值的元素个数
pairs.insert(mmid::value_type(30,111.11));
pairs.insert(mmid::value_type(10,22.22));
pairs.insert(mmid::value_type(25,33.333));
pairs.insert(mmid::value_type(20,9.3));
for( mmid::const_iterator i = pairs.begin();
i != pairs.end() ;i ++ )
cout << "(" << i->first << "," << i->second << ")" << ",";
}
输出:
1) 0
2) 2
(10,22.22),(15,2.7),(15,99.3),(20,9.3),(25,33.333),(30,111.11)

-----------------------------------------------------------
//multimap例题
一个学生成绩录入和查询系统,接受以下两种输入:
Add name id score 输入学生信息 name学生姓名,id学号,score分数。学号不会重复,分数和姓名都可以重复
Query score 查询,输出已有信息中分数比score低的最高分获得者的学生信息,多个学生满足条件,就输出学号最大的,则输出”Noboy“

输入样例:
Add Jack 12 78
Query 78
Query 81
Add Percy 9 81
Add Marry 8 81
Query 82
Add Tom 11 79
Query 80
Query 81

输出果样例:
Nobody
Jack 12 78
Percy 9 81
Tom 11 79
Tom 11 79

class CStudent
{
public:
struct CInfo //类的内部还可以定义类
{
int id;
string name;
};
int score;
CInfo info; //学生的其他信息
};
// 分数 类的内部类 使用要加外部类的名称(学生的id,和学生的名称)
typedef multimap<int, CStudent::CInfo> MAP_STD; //没有指定的比大小的规则,默认为less < 分数
int main() {
MAP_STD mp;
CStudent st;
string cmd;
while( cin >> cmd ) {
if( cmd == "Add") {
cin >> st.info.name >> st.info.id >> st.score ;

//mp.insert(make_pair(st.score,st.info )); 也可以 make_pair 指向pair模版类
mp.insert(MAP_STD::value_type(st.score,st.info ));
}
else if( cmd == "Query" ){
int score;
cin >> score;
//查找一个最大的位置 it,使得[begin(),it) 中所有的元素都比 val 小
MAP_STD::iterator p = mp.lower_bound (score);
if( p!= mp.begin()) {
--p;
score = p->first; //比要查询分数低的最高分
MAP_STD::iterator maxp = p;
int maxId = p->second.id;
for( ; p != mp.begin() && p->first == score; --p) {
//遍历所有成绩和score相等的学生
if( p->second.id > maxId ) {
maxp = p;
maxId = p->second.id ;
}
}
if( p->first == score) {
//如果上面循环是因为 p == mp.begin()
// 而终止,则p指向的元素还要处理
if( p->second.id > maxId ) {
maxp = p;
maxId = p->second.id ;
}
}
cout << maxp->second.name <<
" “ << maxp->second.id << " "
<< maxp->first << endl;
}
else
//lower_bound的结果就是 begin,说明没人分数比查询分数低
cout << "Nobody" << endl;
}
}
return 0;
}

--------------------------------------------------------
map 中的元素都是pair模板类对象。关键字(first成员变量)各不相同。元素按照关键字从小到大排列,缺省情况下用 less<Key>,即“<” 定义“小于”。

template <class Key,class Value>
ostream & operator <<( ostream & o, const pair<Key,Value> & p) //重载,pair对象的内容输出
{
o << "(" << p.first << "," << p.second << ")"; //输出方式
return o;
}
int main() {
// 关键字 比大小方式(<)
typedef map<int, double,less<int> > mmid;
mmid pairs;
cout << "1) " << pairs.count(15) << endl;
pairs.insert(mmid::value_type(15,2.7)); //插入元素
pairs.insert(make_pair(15,99.3)); //make_pair生成一个pair对象
cout << "2) " << pairs.count(15) << endl;
pairs.insert(mmid::value_type(20,9.3));
mmid::iterator i;
cout << "3) "; 1)
for( i = pairs.begin(); i != pairs.end();i ++ )
cout << * i << ",";
cout << endl;
cout << "4) ";
int n = pairs[40];//如果没有关键字为40的元素,则插入一个
for( i = pairs.begin(); i != pairs.end();i ++ )
cout << * i << ",";
cout << endl;
cout << "5) ";
pairs[15] = 6.28; //把关键字为15的元素值改成6.28
for( i = pairs.begin(); i != pairs.end();i ++ )
cout << * i << ",";
}

容器适配器

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
【stack】
头文件 <stack>
栈。是项的有限序列,并满足序列中被删除、检索和修改的项只能是最近插入序列的项(栈顶的项)。"后进先出"

出栈 入栈
↖ ↗
\ /
___________
top——> | an |
———————————
| ... |
———————————
| ... |
———————————
| ... |
———————————
bottom——> | a1 |
———————————

stack 是后进先出的数据结构,只能插入,删除,访问栈顶的元素。
可用 vector, list, deque来实现。缺省情况下,用deque实现。用 vector和deque实现,比用list实现性能好。

stack 上可以进行以下操作:
push 插入元素
pop 弹出元素
top 返回栈顶元素的引用

---------------------------------------------------------------------

【queue】
头文件 <queue>
队列。插入只可以在尾部进行,删除、检索和修改只允许从头部进行。"先进先出"

入队
\

___________
rear——> | an |
———————————
| ... |
———————————
| ... |
———————————
| ... |
———————————
front——> | a1 |
———————————
\

出队

和stack 基本类似,可以用 list和deque实现。缺省情况下用deque实现。
同样也有push, pop, top函数。但是push发生在队尾;pop, top发生在队头。先进先出。
有 back 成员函数可以返回队尾元素的引用

---------------------------------------------------------------------
【priority_queue】
头文件 <queue>
优先级队列。最高优先级元素总是第一个出列

和 queue类似,可以用vector和deque实现。缺省情况下用vector实现。
priority_queue 通常用堆排序技术实现,保证最大的元素总是在最前面。即执行pop操作时,删除的是最大的元素;执行top操作时,返回的是最大元素的常引用。默认的元素比较器是less<T>。

push、pop 时间复杂度O(logn)
top()时间复杂度O(1)

int main()
{
priority_queue<double> pq1;
pq1.push(3.2); pq1.push(9.8); pq1.push(9.8); pq1.push(5.4);
while( !pq1.empty() ) { //判断是否为空
cout << pq1.top() << " "; //队头元素输出(最大的)
pq1.pop(); //队头元素删除(最大的),剩下的元素中最大的到队头
} //上面输出 9.8 9.8 5.4 3.2
cout << endl; //小的反而大,小的排前面
priority_queue<double,vector<double>,greater<double> > pq2; pq2.push(3.2); pq2.push(9.8); pq2.push(9.8); pq2.push(5.4);
while( !pq2.empty() ) {
cout << pq2.top() << " ";
pq2.pop();
}
//上面输出 3.2 5.4 9.8 9.8 return 0;
}
stack , queue , priority_queue 都有的成员函数
empty()成员函数用于判断适配器是否为空
size()成员函数返回适配器中元素个数

算法

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
STL中的算法大致可以分为以下七类:
1) 不变序列算法
2) 变值算法
3) 删除算法
4) 变序算法
5) 排序算法
6) 有序区间算法
7) 数值算法

大多重载的算法都是有两个版本的
1.其中一个是用“==”判断元素是否相等,或用“<”来比较大小;
2.而另一个版本多出来一个类型参数“Pred”,以及函数形参“Pred op”,该版本通过表达式“op(x,y)”的返回值是ture还是false,来判断x是否“等于”y,或者x是否“小于”y。

如下面的有两个版本的min_element:
iterate min_element(iterate first,iterate last);
iterate min_element(iterate first,iterate last, Pred op);

1. 不变序列算法

算法名称功能
min求两个对象中较小的(可自定义比较器)
max求两个对象中较大的(可自定义比较器)
min_element求区间中的最小值(可自定义比较器)
max_element求区间中的最大值(可自定义比较器)
for_each对区间中的每个元素都做某种操作
count计算区间中等于某值的元素个数
count_if计算区间中符合某种条件的元素个数
find在区间中查找等于某值的元素
find_if在区间中查找符合某条件的元素
find_end在区间中查找另一个区间最后一次出现的位置(可自定义比较器)
find_first_of在区间中查找第一个出现在另一个区间中的元素 (可自定义比较器)
adjacent_find在区间中寻找第一次出现连续两个相等元素的位置(可自定义比较器)
search在区间中查找另一个区间第一次出现的位置(可自定义比较器)
search_n在区间中查找第一次出现等于某值的连续n个元素(可自定义比较器)
equal判断两区间是否相等(可自定义比较器)
mismatch逐个比较两个区间的元素,返回第一次发生不相等的两个元素的位置( 可自定义比较器)
lexicographical_compare按字典序比较两个区间的大小(可自定义比较器)
1
2
3
1.此类算法不会修改算法所作用的容器或对象
2.适用于所有容器
3.时间复杂度都是O(n)的

2. 变值算法

1
2
此类算法会修改源区间或目标区间元素的值。
值被修改的那个区间,不可以是属于关联容器的。
算法名称功能
for_each对区间中的每个元素都做某种操作
copy复制一个区间到别处
copy_backward复制一个区间到别处,但目标区前是从后往前被修改的
transform将一个区间的元素变形后拷贝到另一个区间
swap_ranges交换两个区间内容
fill用某个值填充区间
fill_n用某个值替换区间中的n个元素
generate用某个操作的结果填充区间
generate_n用某个操作的结果替换区间中的n个元素
replace将区间中的某个值替换为另一个值
replace_if将区间中符合某种条件的值替换成另一个值
replace_copy将一个区间拷贝到另一个区间,拷贝时某个值要换成新值拷过去
replace_copy_if将一个区间拷贝到另一个区间,拷贝时符合某条件的值要换成新值拷过去

3. 删除算法

算法名称功能
remove删除区间中等于某个值的元素
remove_if删除区间中满足某种条件的元素
remove_copy拷贝区间到另一个区间。等于某个值的元素不拷贝
remove_copy_if拷贝区间到另一个区间。符合某种条件的元素不拷贝
unique删除区间中连续相等的元素,只留下一个(可自定义比较器)
unique_copy拷贝区间到另一个区间。连续相等的元素,只拷贝第一个到目标区间 (可自定义比较器)

4. 变序算法

1
2
变序算法改变容器中元素的顺序,但是不改变元素的值。
变序算法不适用于关联容器。此类算法复杂度都是O(n)的。
算法名称功能
reverse颠倒区间的前后次序
reverse_copy把一个区间颠倒后的结果拷贝到另一个区间,源区间不变
rotate将区间进行循环左移
rotate_copy将区间以首尾相接的形式进行旋转后的结果拷贝到另一个区间,源区间不变
next_permutation将区间改为下一个排列(可自定义比较器)
prev_permutation将区间改为上一个排列(可自定义比较器)
random_shuffle随机打乱区间内元素的顺序
partition把区间内满足某个条件的元素移到前面,不满足该条件的移到后面
stable_patition把区间内满足某个条件的元素移到前面,不满足该条件的移到后面。而且对这两部分元素,分别保持它们原来的先后次序不变
random_shuffletemplate
void random_shuffle(RanIt first, RanIt last);
随机打乱[first,last) 中的元素,适用于能随机访问的容器。
reversetemplate
void reverse(BidIt first, BidIt last);
颠倒区间[first,last)顺序
next_permutationtemplate
bool next_permutaion (Init first,Init last);
求下一个排列

5. 排序算法

1
排序算法比前面的变序算法复杂度更高,一般是O(n×log(n))。排序算法需要随机访问迭代器的支持,因而不适用于关联容器和list。
算法名称功能
sort将区间从小到大排序(可自定义比较器)。
stable_sort将区间从小到大排序,并保持相等元素间的相对次序(可自定义比较器)。
partial_sort对区间部分排序,直到最小的n个元素就位(可自定义比较器)。
partial_sort_copy将区间前n个元素的排序结果拷贝到别处。源区间不变(可自定义比较器)。
nth_element对区间部分排序,使得第n小的元素(n从0开始算)就位,而且比它小的都在它前面,比它大的都在它后面(可自定义比较器)。
make_heap使区间成为一个“堆”(可自定义比较器)。
push_heap将元素加入一个是“堆”区间(可自定义比较器)。
pop_heap从 “堆”区间删除堆顶元素(可自定义比较器)。
sort_heap将一个“堆”区间进行排序,排序结束后,该区间就是普通的有序区间,不再是 “堆”了(可自定义比较器)。
partial_sort部分排序,直到 前 n 个元素就位即可。
nth_element排序,直到第 n个元素就位,并保证比第n个元素小的元素都在第 n 个元素之前即可。
partition改变元素次序,使符合某准则的元素放在前面

6. 有序区间算法

1
有序区间算法要求所操作的区间是已经从小到大排好序的,而且需要随机访问迭代器的支持。所以有序区间算法不能用于关联容器和list。
算法名称功能
binary_search判断区间中是否包含某个元素。
includes判断是否一个区间中的每个元素,都在另一个区间中。
lower_bound查找最后一个不小于某值的元素的位置。
upper_bound查找第一个大于某值的元素的位置。
equal_range同时获取lower_bound和upper_bound。
merge合并两个有序区间到第三个区间。
set_union将两个有序区间的并拷贝到第三个区间
set_intersection将两个有序区间的交拷贝到第三个区间
set_difference将两个有序区间的差拷贝到第三个区间
set_symmetric_difference将两个有序区间的对称差拷贝到第三个区间
inplace_merge将两个连续的有序区间原地合并为一个有序区间
lower_boundtemplate<class FwdIt, class T>
FwdIt lower_bound(FwdIt first, FwdIt last, const T& val);
要求[first,last)是有序的,
查找[first,last)中的,最大的位置 FwdIt,使得[first,FwdIt) 中所有的元素都比 val 小
upper_boundtemplate<class FwdIt, class T>
FwdIt upper_bound(FwdIt first, FwdIt last, const T& val);
要求[first,last)是有序的,
查找[first,last)中的,最小的位置 FwdIt,使得[FwdIt,last) 中所有的元素都比 val 大
equal_rangetemplate<class FwdIt, class T>
pair<FwdIt, FwdIt> equal_range(FwdIt first, FwdIt last, const T& val);
要求[first,last)是有序的,
返回值是一个pair, 假设为 p, 则:
[first,p.first) 中的元素都比 val 小
[p.second,last)中的所有元素都比 val 大
p.first 就是lower_bound的结果
p.last 就是 upper_bound的结果

7. 数值算法

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
template<size_t N>
class bitset
{
...
};

实际使用的时候,N是个整型常数
如:
bitset<40> bst;
bst是一个由40位组成的对象,用bitset的函数可以方便地访问任何一位。

bitset<N>& operator&=(const bitset<N>& rhs);
bitset<N>& operator|=(const bitset<N>& rhs);
bitset<N>& operator^=(const bitset<N>& rhs);
bitset<N>& operator<<=(size_t num);
bitset<N>& operator>>=(size_t num);
bitset<N>& set(); //全部设成1
bitset<N>& set(size_t pos, bool val = true); //设置某位
bitset<N>& reset(); //全部设成0
bitset<N>& reset(size_t pos); //某位设成0
bitset<N>& flip(); //全部翻转
bitset<N>& flip(size_t pos); //翻转某位
reference operator[](size_t pos); //返回对某位的引用
bool operator[](size_t pos) const; //判断某位是否为1
reference at(size_t pos);
bool at(size_t pos) const;
unsigned long to_ulong() const; //转换成整数
string to_string() const; //转换成字符串
size_t count() const; //计算1的个数
size_t size() const;
bool operator==(const bitset<N>& rhs) const;
bool operator!=(const bitset<N>& rhs) const;
bool test(size_t pos) const; //测试某位是否为 1
bool any() const; //是否有某位为1
bool none() const; //是否全部为0
bitset<N> operator<<(size_t pos) const;
bitset<N> operator>>(size_t pos) const;
bitset<N> operator~();
static const size_t bitset_size = N;
注意:第0位在最右边

c++ 11特性

成员变量默认初始值

1
2
3
4
5
6
7
8
9
10
class B {
public:
int m = 1234; int n;
};
int main()
{
B b;
cout << b.m << endl; //输出 1234
return 0;
}

auto关键字

用于定义变量,编译起可以自动判断变量的类型,需要初始化

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
auto i = 100;      // i 是 int
auto p = new A(); // p 是 A *
auto k = 34343LL; // k 是 long long

---------------------------------------------

map<string,int,greater<string> > mp;
for( auto i = mp.begin(); i != mp.end(); ++i)
cout << i->first << "," << i->second ;
//i的类型是: map<string,int,greater<string> >::iterator

---------------------------------------------
class A { };
A operator + ( int n,const A & a)
{
return a;
}
template <class T1, class T2>
//函数的返回值写在函数的后面用 ->
auto add(T1 x, T2 y) -> decltype(x + y){ // decltype确定x+y的类型,将该类型返回给 auto 确定函数类型
return x+y;
}
auto d = add(100,1.5); // d是double d=101.5
auto k = add(100,A()); // k是A类型

decltype 关键字

求表达式的类型

1
2
3
4
5
6
7
8
int i; 
double t;
struct A { double x; };
const A* a = new A();
decltype(a) x1; //x1 是 A* 类型
decltype(i) x2; //x2 是 int 类型
decltype(a->x) x3; //x3 是 double 类型
decltype((a->x)) x4 = t; //x4 是 double& 类型 用括号(),包括,就是&

基于范围的for循环

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16

struct A { int n; A(int i):n(i) {} };
int main() {
int ary[] = {1,2,3,4,5};
for(int & e: ary) // 遍历,&e,就是arr[]里面每一个值,e改变那么arr里面对应的值就改变
e*= 10;
for(int e : ary) // 遍历,e,复制arr[]里面每一个值来使用,e不会改变arr里面的值
cout << e << ",";
cout << endl;
vector<A> st(ary,ary+5);
for( auto & it: st)
it.n *= 10;
for( A it: st)
cout << it.n << ",";
return 0;
}

右值引用和MOVE语义

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
&& 右值:一般来说,不能取地址的表达式,就是右值,能取地址的,就是左值

class A { };
A & r = A(); // error , A()是无名变量,是右值
A && r = A(); //ok, r 是右值引用

主要目的是提高程序运行的效率,减少需要进行深拷贝的对象进行深拷贝的次数。

'move 将左值变为右值'

-------------------------------------
class String
{
public:
char * str;
String():str(new char[1]) { str[0] = 0;}
String(const char * s) {
str = new char[strlen(s)+1];
strcpy(str,s);
}
String(const String & s) {
cout << "copy constructor called" << endl;
str = new char[strlen(s.str)+1];
strcpy(str,s.str);
}
String & operator=(const String & s) {
cout << "copy operator= called" << endl;
if( str != s.str) {
delete [] str;
str = new char[strlen(s.str)+1];
strcpy(str,s.str);
}
return * this;
}
// move constructor
String(String && s):str(s.str) {

cout << "move constructor called"<<endl;
s.str = new char[1];
s.str[0] = 0;
}
String & operator = (String &&s) {
cout << "move operator= called"<<endl;
if (str!= s.str) {
delete [] str;
str = s.str;
s.str = new char[1];
s.str[0] = 0;
}
return *this;
}
~String() { delete [] str; }
};
template <class T>
void MoveSwap(T& a, T& b){
T tmp(move(a)); // std::move(a)为右值,这里会调用moveconstructor
a = move(b); //move(b)为右值,因此这里会调用move assigment
b = move(tmp); // move(tmp)为右值,因此这里会调用move assigment
}
int main(){
//String & r = String("this"); // error
String s;
s = String("ok"); // String("ok")是右值
cout << "******" << endl;
String && r = String("this");
cout << r.str << endl;
String s1 = "hello",s2 = "world";
MoveSwap(s1,s2);
cout << s2.str << endl;
return 0;
}

无序容器(哈希表)

头文件 <unordered_map>

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
int main()
{
unordered_map<string,int> turingWinner; //图灵奖获奖名单
turingWinner.insert(make_pair("Dijkstra",1972));
turingWinner.insert(make_pair("Scott",1976));
turingWinner.insert(make_pair("Wilkes",1967));
turingWinner.insert(make_pair("Hamming",1968));
turingWinner["Ritchie"] = 1983;
string name;
cin >> name; //输入姓名
unordered_map<string,int>::iterator p = turingWinner.find(name);
//据姓名查获奖时间
if( p != turingWinner.end())
cout << p->second;
else
cout << "Not Found" << endl;
return 0;
}


哈希表插入和查询的时间复杂度几乎是常数

正则表达式

头文件

1
2
3
4
5
6
7
8
9
10
11
12
int main()
{
regex reg("b.?p.*k");
cout << regex_match("bopggk",reg) <<endl;//输出 1, 表示匹配成功
cout << regex_match("boopgggk",reg) <<endl;//输出 0, 匹配失败
cout << regex_match("b pk",reg) <<endl; //输出 1, 表示匹配成功
regex reg2("\\d{3}([a-zA-Z]+).(\\d{2}|N/A)\\s\\1");
string correct="123Hello N/A Hello";
string incorrect="123Hello 12 hello";
cout << regex_match(correct,reg2) <<endl; //输出 1,匹配成功
cout << regex_match(incorrect,reg2) << endl; //输出 0, 失败
}

Lambda表达式

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
形式:
[外部变量访问方式说明符](参数表) ->返回值类型
{
语句组
}

[] 不使用任何外部变量
[=] 以传值的形式使用所有外部变量
[&] 以引用形式使用所有外部变量
[x, &y] x 以传值形式使用,y 以引用形式使用
[=,&x,&y] x,y 以引用形式使用,其余变量以传值形式使用
[&,x,y] x,y 以传值的形式使用,其余变量以引用形式使用

------------------------------------------------------

int main() {
int x = 100,y=200,z=300;
cout << [](double a,double b) { return a + b; }(1.2,2.5) << endl;
auto ff = [=,&y,&z](int n) {
cout << x << endl;
y++; z++;
return n*n;
};
cout << ff(15) << endl;
cout << y << "," << z << endl;
}

-----------------------------------------------------

int a[4] = { 4,2,11,33};
sort(a,a+4,[ ](int x,int y)->bool { return x%10 < y%10; });
for_each(a,a+4,[ ](int x) {cout << x << " " ;} ) ;

-----------------------------------------------------

function<int(int)>fib=[&fib](int n)
{ return n <=2 ? 1:fib(n-1)+fib(n-2);};

cout<<fib(5)<<endl; //输出5

function<int(int)> 表示返回值为 int,有一个 int 参数的函数

强制类型转换

static_cast
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
static_cast用来进用行比较“自然”和低风险的转换,比如整型和实数型、字符型之间互相转换。
static_cast不能来在不同类型的指针之间互相转换,也不能用于整型和指针之间的互相转换,也不能用于不同类型的引用之间的转换。

class A
{
public:
operator int() { return 1; }
operator char * (){ return NULL; }
};
int main(){
A a;
int n; char * p = "New Dragon Inn";
n = static_cast<int>(3.14); // n 的值变为 3
n = static_cast<int>(a); //调用a.operator int, n的值变为 1
p = static_cast<char*>(a);
//调用a.operator int *,p的值变为 NULL
n = static_cast<int> (p);
//编译错误,static_cast不能将指针转换成整型
p = static_cast<char*>(n);
//编译错误,static_cast不能将整型转换成指针
return 0;
}
reinterpret_cast
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
reinterpret_cast用来进行各种不同类型的指针之间的转换、不同类型的引用之间转换、以及指针和能容纳得下指针的整数类型之间的转换。转换的时候,执行的是逐个比特拷贝的操作。

class A
{
public:
int i;
int j;
A(int n):i(n),j(n) { }
};
int main() {
A a(100);
int & r = reinterpret_cast<int&>(a); //强行让 r 引用 a
r = 200; //把 a.i 变成了 200
cout << a.i << "," << a.j << endl; // 输出 200,100
int n = 300;
A * pa = reinterpret_cast<A*> ( & n); //强行让 pa 指向 n
pa->i = 400; // n 变成 400
pa->j = 500; //此条语句不安全,很可能导致程序崩溃
cout << n << endl; // 输出 400
long long la = 0x12345678abcdLL;
pa = reinterpret_cast<A*>(la);
// la太长,只取低32位0x5678abcd拷贝给pa
unsigned int u = reinterpret_cast<unsigned int>(pa);
//pa逐个比特拷贝到u
cout << hex << u << endl; //输出 5678abcd
typedef void (* PF1) (int);
typedef int (* PF2) (int,char *);
PF1 pf1; PF2 pf2;
pf2 = reinterpret_cast<PF2>(pf1);
//两个不同类型的函数指针之间可以互相转换
}
const_cast
1
2
3
4
5
用来进行去除const属性的转换。将const引用转换成同类型的非const引用,将const指针转换为同类型的非const指针时用它。例如:

const string s = “Inception”;
string & p = const_cast<string&>(s);
string * ps = const_cast<string*>(&s); // &s的类型是const string *
dynamic_cast
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
dynamic_cast专门用于将多态基类的指针或引用,强制转换为派生类的指针或引用,而且能够检查转换的安全性。对于不安全的指针转换,转换结果返回NULL指针。
dynamic_cast不能用于将非多态基类的指针或引用,强制转换为派生类的指针或引用

class Base
{ //有虚函数,因此是多态基类
public:
virtual ~Base() { } };
class Derived:public Base {};
int main()
{
Base b;
Derived d;
Derived * pd;
pd = reinterpret_cast<Derived*> ( &b);
if( pd == NULL)
//此处pd不会为NULL。reinterpret_cast不检查安全性,总是进行转换
cout << "unsafe reinterpret_cast" << endl; //不会执行
pd = dynamic_cast<Derived*> ( &b);
if( pd == NULL)
//结果会是NULL,因为 &b不是指向派生类对象,此转换不安全
cout << "unsafe dynamic_cast1" << endl; //会执行
pd = dynamic_cast<Derived*> ( &d); //安全的转换
if( pd == NULL)//此处pd 不会为NULL
cout << "unsafe dynamic_cast2" << endl; //不会执行
return 0;
}

异常处理

1
2
3
1.一个函数运行期间可能产生异常。在函数内部对异常进行处理未必合适。因为函数设计者无法知道函数调用者希望如何处理异常。
2.告知函数调用者发生了异常,让函数调用者处理比较好
3.用函数返回值告知异常不方便
用try、catch进行异常处理
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
107
108
109
int main()  
{
double m ,n;
cin >> m >> n;
try {
cout << "before dividing." << endl;
if( n == 0)
throw -1; //抛出int类型异常
else
cout << m / n << endl;
cout << "after dividing." << endl;
}
catch(double d) {
cout << "catch(double) " << d << endl;
}
catch(int e) {
cout << "catch(int) " << e << endl;
}
cout << "finished" << endl;
return 0;
}

----------------------------------------------------

【捕获任何异常的catch块】
int main()
{
double m ,n;
cin >> m >> n;
try {
cout << "before dividing." << endl;
if( n == 0)
throw -1; //抛出整型异常
else if( m == 0 )
throw -1.0; //抛出double型异常
else
cout << m / n << endl;
cout << "after dividing." << endl;
}
catch(double d) {
cout << "catch(double) " << d << endl;
}
catch(...) {
cout << "catch(...) " << endl;
}
cout << "finished" << endl;
return 0;
}

注意:try块中定义的局部对象,发生异常时会析构!

程序运行结果:
9 0
before dividing.
catch(...)
finished

0 6
before dividing.
catch(double) -1
finished
----------------------------------------------------
【异常的再抛出】
如果一个函数在执行的过程中,抛出的异常在本函数内就被catch块捕获并处理了,那么该异常就不会抛给这个函数的调用者(也称“上一层的函数”);如果异常在本函数中没被处理,就会被抛给上一层的函数。
class CException
{
public :
string msg;
CException(string s):msg(s) { }
};
double Devide(double x, double y){
if(y == 0)
throw CException("devided by zero");
cout << "in Devide" << endl;
return x / y;
}
int CountTax(int salalry)
{
try{
if(salary < 0 )
throw - 1;
cout << "countiong tax"<<endl;
}
catch (int){
cout << "salary < 0" << endl;
}
cout << "tax counted" << endl;
return salary * 0.15;
}
int main() {
double f = 1.2;
try {
CountTax(-1);
f = Devide(3,0);
cout << "end of try block" << endl;
}
catch(CException e) {
cout << e.msg << endl;
}
cout << "f=" << f << endl;
cout << "finished" << endl;
return 0;
}

输出结果:
salary < 0
tax counted
devided by zero f=1.2
finished
bad_cast
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
在用dynamic_cast 进行从多态基类对象(或引用),到派生类的引用强制类型转换时

class Base
{
virtual void func(){}
};
class Derived : public Base
{
public:
void Print() { }
};
void PrintObj( Base & b){
try {
Derived & rd = dynamic_cast<Derived&>(b);
//此转换若不安全,会抛出bad_cast异常
rd.Print();
}
catch (bad_cast& e) {
cerr << e.what() << endl;
}
}
int main ()
{
Base b;
PrintObj(b);
return 0;
}

输出结果:
Bad dynamic_cast!
bad_alloc
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
在用new运算符进行动态内存分配时,如果没有足够的内存,则会引发此异常。

int main ()
{
try {
char * p = new char[0x7fffffff];
//无法分配这么多空间,会抛出异常
}
catch (bad_alloc & e){
cerr << e.what() << endl;
}
return 0;
}

输出结果:
bad allocation
out_of_range
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
用vector或string的at成员函数根据下标访问元素时,如果下标越界,就会抛出此异常。
int main ()
{
vector<int> v(10);
try {
v.at(100)=100; //抛出out_of_range异常
}
catch (out_of_range& e) {
cerr << e.what() << endl;
}
string s = "hello";
try {
char c = s.at(100); //鎶涘嚭out_of_range寮傚父
}
catch (out_of_range& e) {
cerr << e.what() << endl;
}
return 0;
}

输出结果:
invalid vector<T> subscript
invalid string position