4.C++深入理解 面向对象部分3

C++深入理解 面向对象部分

一、C++流的控制

1.1 输入输出流相关的类

  输入输出重定向例子,

#include <iostream>
using namespace std;
int main(){
	int x, y;
	cin >> x >> y;
	freopen("test.txt", "w", stdout); // 从t.txt读数据
	if(y == 0)
		cerr << "error." << endl;
	else
		cout << x/y;
	return ;
}
/*
t.txt
3.14 123
输出:
3.14 123
*/

  判断输入流结束。怎么知道 cin 读取文件数据什么时候结束了。用循环方式读入。从文件读 freopen(“some.txt”, “r”, stdin); 那么读到文件尾部,输入就算结束。
  istream 类的成员函数,

istream & getline(char * buf, int bufSize); // 读取 bufSize-1 字符,‘\n'结束
istream & getline(char * buf, int bufSize, char delim); // 遇到 delim 字符结束

读入都会自动添加 ’\0',可以用 if(!cin.getline()) 判断是否结束
bool eof(); // 判断输入流是否结束
int peek(); // 返回下一个字符,但不从流中去掉
istream & putback(char c); // 将字符 ch 放回输入流
istream & ignore(int nCount = 1, int delim = EOF); // 从流中删掉最多 nCount 个字符,遇到 EOF 时结束。

  举个例子,

#include <iostream>
using namespace std;
int main(){
	int x, y;
	char buf[100];
	cin >> x;
	cin.getline(buf, 90);
	cout << buf << endl;
	return 0;
}

/*
输入:
12 abcd 回车
输出:
 abcd // 注意是读入是字符

输入:
12回车
程序立即结束,输出:
 (什么都没有) // 注意读入字符
*/
1.2 流操纵算子控制输出格式

  流操纵算子,设置数据输出格式,使用流操作算子需要 #include < iomanip >.比如,

int n = 10;
cout << n << endl;
cout << hex << n << "\n"
     << dec << n << "\n"
     << oct << n << endl; 
     // 16进制,10进制,8进制输出。知道用另外一个流操作算子,那样作用才会消失

  控制浮点数精度的流操作算子,

precision, setprecision
-> precision 是成员函数, 调用方式为: cout.precision(5);
-> setprecision 是流操作算子,调用方式为: cout << setprecision(5); // 可以连续输出

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

double x = 1234567.89, y = 12.34567;
int n = 1234567;
int m = 12;
// 浮点数输出最多 6 位有效数字
cout << setprecision(6) << x << endl
     << y << endl << n << endl << m;

输出为:
1.23457e+006
12.3457
1234567
12

// 以小数点位置固定的方式输出
cout << setiosflags(ios::fixed) << setprecision(6) << x << endl
     << y << endl << n << endl << m;
输出为:
1234567.890000
12.345670
1234567
12

// 取消以小数点位置固定方式输出
cout << setiosflags(ios::fixed) << setprecision(6) << x << endl
     resetiosflags(ios::fixed) << x;
输出为:
1234567.890000
1.23457e+006

  设置域宽的流操纵算子
—> 设置域宽(setw, width)

int w = 4;
char string[10];
cin.width(5);
while(cin >> string){
	cout.width(w++);
	cout << string << endl;
	cin.width(5);
}
// 宽度设置有效是一次性的,在每次读入和输出之前都要设置宽度
// 缺省情况下,宽度不足,右侧补空格,满足宽度
// 设置域宽时候包含结尾的'\0'
输入:
1234567890
输出:
1234
 5678
    90

  一个综合的例子,

#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 << " " << dec << y << " " << " " << endl;

    // 3) 保留小数点后面 5 位,定点方式输出
    cout << "3)" << fixed << x << 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;
}

结果如下,
在这里插入图片描述
  用户自定义流操纵算子,

ostream &tab(ostream & output){
	return output << '\t';
}
// tab 是函数,当作流操纵算子
cout << "a" << tab << "b" << end;
输出:
aa  bb
为什么可以?
函数要满足,参数为 引用,输出也为 引用

因此,定义方式为,

iostream 里对 << 进行了重载(成员函数)

ostream & operator
<< (ostream &(*p)(ostream &));

该函数内部会调用 p 所指向的函数,且 *this 作为参数 hex, dec, oct 都是函数,且以 *this 作为参数
1.3 文件读写一

  可以将顺序文件看作一个有限字符构成的顺序字符流,然后像对 cin, cout 一样的读写。回顾一下输入输出流类的结构层次。
  包含头文件,#include < fstream >, 如下,

ofstream outFile("clients.dat", ios::out|ios::binary);
--"clients.dat": 要创建的文件名字
--ios::out:      文件的打开方式
     .ios:out    输出到文件,删除原有内容
     .ios::app   输出到文件,保留原有内容,总是在尾部添加
--ios::binary:   以二进制文件格式打开文件

  创建文件
------》也可以先创建 ofstream 对象,再用 open 函数打开
  ofstream fout;
  fout.open(“test.out”, ios::out | ios::binary);
------》判断打开是否成功:
  if(! out){
  cout << “File open error! << endl” }
------》文件名可以给出绝对路径,也可以给出相对路径。没有交代路径信息,就是在当前文件夹下找文件。
  文件名的绝对路径和相对路径,

绝对路径:
"c:\\tmp\\mydir\\some.txt"
相对路径:
"\\tmp\\mydir\\some.txt"  当前盘符的根目录下的 tmp\dir\some.txt
"..\\tmp\\mydir\\some.txt"  当前文件夹的父文件夹下面的tmp子文件夹里面的...
"..\\..\\tmp\\mydir\\some.txt"  当前文件夹的父文件夹的父文件下面的tmp子文件夹里面的...

  文件的读写指针
------》对于输入文件,输出文件,输入输出文件,都有相应的指针,标识文件操作的当前位置,该指针在哪里,读写操作就在哪里进行。举个例子,

/*写指针例子*/
ofstream fout("a1.out", ios::app);  // 以添加方式打开
long location = fout.tellp();  // 取得写指针的位置
location = 10;  // 可以为 负
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 = fout.tellg();  // 取得读指针的位置
location = 10L;  // 可以为 负
fout.seekg(location); // 将读指针移动到第 10 个字节处
fout.seekg(location, ios::beg); // 从头数 location
fout.seekg(location, ios::cur); // 从当前位置数 location
fout.seekg(location, ios::end); // 从尾部数 location

  字符文件读写
------》因为文件流也是流,所以流的成员函数和流操作算子也同样适用于文件流。举个例子,

写一个程序,将文件 in.txt 里面的整数排序后,输出到 out.txt
例如,
若 in.txt 的内容为:1 234 9 45 6 879
生成 out.txt 的内容为:1 6 9 45 234 879

程序如下,

#include <iostream>
#include <fstream>
#include <vector>
#include <algorithm>

using namespace std;

int main(){
    vector<int> v; // 可变长数组,都是 int 类型

    ifstream srcFile("in.txt", ios::in);
    ofstream destFile("out.txt", ios::out);

    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] << " ";
    srcFile.close();  // 处理完成以后一定要关闭
    destFile.close();

    return 0;
}

结果如下,
在这里插入图片描述

1.4 文件读写二

  二进制文件读
------》ifstream 和 fstream 的成员函数,
------》istream & read(char *s, long n);
将文件读指针指向的地方的 n 个字节内容,读入到内存地址 s ,然后将文件读指针向后移动 n 字节 (以 ios::in 方式打开文件时,文件读指针开始指向文件开头)
  二进制文件读
------》ifstream 和 fstream 的成员函数,
------》istream & write(const char *s, long n);
将内存地址 s 处的 n 个字节内容,写入到文件中写指针指向的位置,然后将文件写指针向后移动 n 字节(以 ios::out 方式打开文件时,文件写指针开始指向文件开头,以 ios::app 方式打开文件时,文件写指针开始指向文件尾部)。
  具体例子:在文件中写入和读取一个整数

#include <iostream>
#include <fstream>
#include <vector>
#include <algorithm>
 
using namespace std;
 
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;
}

  具体例子:从键盘输入几个学生的名字的成绩,并以二进制文件形式保存

#include <iostream>
#include <fstream>
#include <vector>
#include <algorithm>

using namespace std;

struct Student{
    char name[20];
    int score;
};
int main(){
    Student s;
    ofstream OutFile("D:\\GraduateLevelFile\\Program\\C++\\Advance\\Deep\\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字节,用记事本打开,呈现的是乱码

接着读取,显示出来,
// 接着再读取出来,程序如下
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;
}
输出:
Tom 60
Jack 80
Jane 40

  具体例子:二进制文件读写,将students.dat文件的Jane改成Mike

// 将students.dat文件的Jane改成Mike
int main(){
    Student s;
    fstream ioFile("D:\\GraduateLevelFile\\Program\\C++\\Advance\\Deep\\students.dat", ios::in | ios::out | ios::binary);
    if(!ioFile){
        cout << "error" << endl;
        return 0;
    }
    ioFile.seekp(2*sizeof(s), ios::beg); // 定位写指针到第三个记录
    ioFile.write("Mike", strlen("Mike")+1); // 结尾 '\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

  总的来说,为什么存为二进制文件,因为节省空间。因为二进制方式存储,字节数固定查找的比较快,比直接存储方式方便很多

  具体例子:文件拷贝程序 mycopy 示例

/* 
  mycopy src.dat dest.dat
  即将 src.dat 拷贝到 dest.dat 如果 dest.dat 原来就有,则原来的文件就会被覆盖
*/
#include <iostream>
#include <fstream>

using namespace std;

int main(int argc, char *argv[]){
    if(argc != 3){
        cout << "File name missing!" << endl;
        return 0;
    }
    // src.dat
    ifstream inFile(argv[1], ios::binary | ios::in); // 打开文件用于读
    if(! inFile){
        cout << "Source file open error." << endl;
        return 0;
    }
    // dest.dat
    ofstream outFile(argv[2], ios::binary | ios::out); // 打开文件用于写
    if(!outFile){
        cout << "New file open error." << endl;
        inFile.close(); // 打开的文件一定要关闭
        return 0;
    }
    char c;
    while(inFile.get(c)); // 每次读取一个字符
        outFile.put(c);  // 每次写入一个字符
    outFile.close();
    inFile.close();
    return 0;
}

  二进制文件和文本文件的区别,

Linux, Unix 下的换行符号:'\n' (ASCII码: 0x0a)
Windows 下的换行符号:'\r\n' (ASCII码: 0x0d0a) endl 就是 '\n'
Mac OS 下的换行符号:'\r' (ASCII码: 0x0d)
导致 Linux, Mac OS 文本文件在 Windows 记事本中打开时不换行

在 Unix/Linux 下打开文件,用不用 ios::binary 没区别
Windows 下打开文件,如果不用 ios::binary, 则:
	读取文件时,所有的'\r\n'会被当作一个字符'\n'处理,即少读一个字符'\r'
	写入文件时,写入单独的'\n'时,系统自动在前面加一个'\r',即多谢一个'\r'

所以用二进制方式读写,会造成很大的麻烦

二、函数模板和类模板

2.1 函数模板

  比如交换两个整型变量的值,写一个 Swap 函数,对于不同类型数据的交换,都要写一个相应的函数,这样就很麻烦,因此引入函数模板。解决方法如下,

template <class 类型参数1, class 类型参数2, ......>
返回值类型 模板名(形参表){
	函数体
};

template <class T>
void Swap(T & x, T & y){
	T temp = x;
	x = y;
	y = temp;
}

函数模板中可以有不止一个类型参数,
tempate <class T1, class T2>
T2 print(T1 arg1, T2 arg2){
	cout << arg1 << " " << arg2 << endl;
	return arg2;
}

  举个例子,求数组最大值的函数,

template <class T>
T maxElement(T a[], int size) // size 是数组元素个数
{
	T tempMax = a[0];
	for(int i = 1; i < size; ++i){
		if(tempMax < a[i])
			tempMax = a[i];
	}
	return tempMax;
}

// 再例如,不通过参数实例化函数模板
T Inc(T n){
	return 1 + n;
}
int main(){
	cout << Inc<double>(4)/2; // 输出 2.5
	return 0;
}

  同时,函数模板是可以重载的,只是他们的形参表或类型参数表不同即可。**函数模板和函数的次序:**先找完全匹配普通函数,再找参数完全匹配的模板函数,最后再找经过自动类型转换后能够匹配的普通函数。举个例子,

template <class T>
T Max(T a, T b){
	cout << "TemplateMax" << endl; return 0;
}
template <class T, class T2>
T Max(T a, T2 b){
	cout << "TemplateMax2" << endl; return 0;
}
double Max(double a, double b){
	cout << "Mymax" << endl; return 0;
}

int main(){
	int i = 4, j = 5;
	Max(1.2, 3.4); // 输出 Mymax
	Max(i, j); // 输出 TemplateMax
	Max(1.2, 3); // 输出 TemplateMax2
	return 0;
}

  匹配函数模板时,不进行类型自动转换。再举个例子,

#include <iostream>
using namespace std;

template <class T, class Pred>
void Map(T s, T e, T x, Pred 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);
    /* 实例化以下函数
    // double (*op)(double) 是函数指针
	void Map(int * s, int * e, int *x, double (*op)(double)){
		for(; s != e; ++s, ++x) *x = op(*s);
	}*/
	for(int i = 0; i < 5; ++i)cout << b[i] << ",";
	cout << endl;

	Map(a, a+5, b, Cube);
    /* 实例化以下函数
    // double (*op)(double) 是函数指针
	void Map(int * s, int * e, int *x, double (*op)(double)){
		for(; s != e; ++s, ++x) *x = op(*s);
	}*/
	for(int i = 0; i < 5; ++i)cout << b[i] << ",";
	cout << endl;

	Map(d, d+5, c, Square);
    /* 实例化以下函数
    // double (*op)(double) 是函数指针
	void Map(int * s, int * e, int *x, double (*op)(double)){
		for(; s != e; ++s, ++x) *x = op(*s);
	}*/
	for(int i = 0; i < 5; ++i)cout << c[i] << ",";
	cout << endl;

	return 0;
}

  结果如下,
在这里插入图片描述

2.2 类模板

  为了多快好省地定义出一批相似的类,可以定义类模板,然后由类模板生成不同的类。一个类的元素和函数,使用模板方式实例化以后,可以自动生成各种类型的元素和函数。定义如下,

template <typename 类型参数1typename 类型参数2, ......>
// 类型参数表
class 类模板名{
	成员函数和成员变量
};

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

// 用类模板定义对象的写法:
类模板名 <真实类型参数表> 对象名(构造函数实参表);

  举个例子,

#include <iostream>
using namespace std;

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>
// Pair 的成员函数 operator <
// Pair <T1, T2> 类的名字
bool Pair <T1, T2>::operator <(const Pair <T1, T2> & p) const{
    return key < p.key; // 谁的 key 小谁算小
}

int main(){
    Pair <string, int> student("Tom", 19);
    // 实例化出一个类 Pair <string, int>
    cout << student.key << " " << student.value;

    return 0;
}

  结果如下,
在这里插入图片描述
  编译器由类模板生成类的过程叫做类模板的实例化。有类模板实例化得到的类,叫做模板类。同一个类模板的两个模板类是不兼容的,比如

Pair <string, int> *p;
Pair <string, double> a;
p = & a; // 会出错,不兼容

  函数模板可以作为类模板成员

#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 被实例化 char
    a.Func("hello"); // 成员函数模板 Func 再此被实例化 char *

    return 0;
    // 输出为: Khello
}

  类模板与非类模板
  类模板的 “<类型参数表>” 中可以出现非类型参数:

// int size 是非类型参数
template <class T, int size>
class Array{
	T array[size];
	public:
		void Print(){
			for(int i = 0; i < size; ++i){
				cout << array[i] << endl;
			}
		}
};
Array <double, 40> a1;
Array <double, 70> a2;  // a1 和 a2 属于不同的类
Array <int, 50> a3;  // a2 和 a3 属于不同的类
2.3 类模板与派生、友元

  类模板与继承

// 类模板从类模板派生
// 类模板从模板类派生
// 类模板从普通类派生
// 普通类从模板类派生

// 1. 类模板从类模板派生
template <class T1, class T2>
class A{
	T1 v1; T2 v2;
};
template <class T1, class T2>
class B: public A<T2, T1>{
	T1 v3; T2 v4;
};
template <class T>
class C: public B<T, T>{
	T v5;
};

int main(){
	B<int, double> obj1;
	C<int> obj2;
	
	return 0;
}

// 2. 类模板从模板类派生
template <class T1, class T2>
class A{
	T1 v1; T2 v2;
};
template <class T>
class B: public A <int, double>{
	T v;
};
int main(){
	B <char> obj1; // 自动生成两个模板类: A <int, double> 和 B <char>
	return 0;
}

// 3. 类模板从普通类派生
class A{
	int v1;
};
template <class T>
class B: public A{  // 所有从 B 实例化得到的类,都以 A 为基类
	T v;
};
int main(){
	B <char> obj1;
	return 0;
}

// 4. 普通类从模板类派生
template <class T>
class A{
	T v1; int n;
};
class B: public A<int>{
	double v;
};
int main(){
	B obj1;
	return 0;
}

  类模板与友元

// 函数、类、类的成员函数作为类模板的友元
// 函数模板作为类模板的友元
// 函数模板作为类的友元
// 类模板作为类模板的友元

// 1. 函数、类、类的成员函数作为类模板的友元
void Func1(){}
class A{};
class B{
	public:
		void Func(){}
};
template <class T>
class Temp1{
	friend void Func1();
	friend class A;
	friend class B::Func();
}; // 任何从 Temp1 实例化来的类,都有以上三个友元

// 2. 函数模板作为类模板的友元
#include <iostream>

using namespace std;

template <class T1, class T2>
class Pair{
	private:
		T1 key;  // 关键字
		T2 value;  // 值
	public:
		Pair(T1 k, T2 v):key(k), value(v){};
		bool operator < (const Pair <T1, T2> & p)const;
		template <class T3, class T4> // 新的模板
		friend ostream & operator << (ostream & o,
								const Pair <T3, T4> &p);
};
template <class T1, class T2>
bool Pair <T1, T2>::operator < (const Pair <T1, T2> & p)const{
	// “小” 的意思就是关键字小
	return key < p.key;
}
template <class T1, class T2> // 与之前模板一样
ostream & operator << (ostream & o,
		const Pair <T1, T2> & p){
	o << "(" << p.key << "," << p.value << ")";
	return o;
}

int main(){
    Pair <string, int> student("Tom", 29);
    Pair <int, double> obj(12, 3.14);
    cout << student << " " << obj;

    return 0;
}
输出为:
(Tom,29) (12,3.14)

注意:任意从 template <class T1, class T2> 
ostream & operator << (ostream & o,const Pair <T1, T2> & p)
生成的函数,都是任意 Pair 模板类的友元


// 3. 函数模板作为类的友元
#include <iostream>

using namespace std;
class A{
	int v;
	public:
		A(int n):v(n){}
		template <class T>
		friend void Print(const T & p);
};
template <class T>
void Print(const T & p){
	cout << p.v;
}
int main(){
	A a(4);
	Print(a);

	return 0;
}
输出:
4
注意:所有从 template <class T>
void Print(const T & p) 生成的函数,都成为 A 的友元
但是自己写的函数
void Print(int a){}
不会成为 A 的友元

// 4. 普通类从模板类派生
#include <iostream>

using namespace std;
template <class T>
class B{
	T v;
	public:
		B(T n):v(n){}
		template <class T2>
		friend class A;
};
template <class T>
class A{
	public:
		void Func(){
			B <int> o(10);
			cout << o.v << endl;
		}
};

int main(){
	A <double> a;
	a.Func();
	
	return 0;
}
输出:
10
A <double> 类,成为 B <int> 类的友元。
任何从 A 模板实例化出来的类,都是任何 B 实例化出来的类的友元
2.4 类模板与静态成员变量

  类模板与 static 成员
  类模板中可以定义静态成员,那么从该类模板实例化得到的所有类,都包含同样的静态成员。比如,

#include <iostream>

using namespace std;

template <class T>
class A{
    private:
        static int count;
    public:
        A(){count ++;}
        ~A(){count --;}
        A(A &){count ++;}
        static void printCount(){cout << count << endl;}
};
// 在 dev c++ 要写 template<> 
template<> int A<int>::count = 0;
template<> int A<double>::count = 0;
int main(){
    A <int> ia;
    A <double> da;
    ia.printCount();
    da.printCount();

    return 0;
}
输出:
1
1

三、string类

  string 类是模板类,使用 “string” 类要包含头文件 < string >,string 对象初始化:

// 正确初始化方法
string s1("Hello");
string month = "March";
string s2(8, 'x');

// 错误初始化方法
string s1 = 'c';
string s2('u');
string s3 = 22;
string s4(8);

// 但是可以将字符赋值给 string 对象
string s
s = 'n';

  string 类支持

// 常用
1. string 对象的长度用函数 length() 读取
2. string 支持流读取运算符
3. string 支持 getline 函数 // string s; getline(cin, s);
4. string 对象可以用 = 赋值
5. string 用 assign 成员函数复制 // string s1("cat"), s3; s3.assign(s1);
6. string 用 assign 成员函数部分复制 // string s1("catpig"), s3; s3.assign(s1, 1, 3);
7. string 单个字符复制 // s2[5] = s1[3] = 'a';
8. string 逐个访问 string 对象中的字符 // string s; s.at(i)... at 会越界检查
9. string 用 + 运算符连接字符串
10. string 用成员函数 append 连接字符串 // string s1, s2; s1.append(s2, 1, s2.size())
11. string 可以比较大小,返回值为 bool 类型
12. string 用成员函数 compare 比较 string 大小 // string s1, s2; s1.compare(s2, 1, 3)
13. string 成员函数 substr, 取字串 // string s1, s2; s2 = s1.substr(3, 4)
14. string 成员函数 swap, 交换两个字符串 // s1.swap(s2)
15. string 成员函数 find, 查找一个字符串位置,返回首部下标,找不到返回string::npos
       也可以选择从某一个位置开始找 // string s; s.find("abc", 1);
16. string 成员函数 rfind, 从后往前找,也是返回首部下标,找不到返回string::npos

// 不常用
17. string find_first_of,查找字符串中任何一个字符第一次出现位置,找不到返回string::npos
18. string find_last_of,查找字符串中任何一个字符最后一次出现位置,找不到返回string::npos
19. string find_first_not_of
20. string find_last_not_of

  对字符串进行操作的函数

/*注意字符下标是从 1 开始,不是从 0 开始*/
// 删除 string 中的字符
erase() // s1.erase(5) 删除下标 5 及以之后的字符

// 替换 string 中的字符 
replace // s1.replace(2, 3, "haha") 将 2-3 下标字符替换成 "haha"
        // s1.replace(2, 3, "haha", 1, 2) 将 2-3 下标字符替换成 "haha" 中 1-2 字符

// 在 string 插入字符
insert // s1.insert(5, s2) 从 5 开始插入 s2

// 转换成 C 语言 char* 字符串
c_str()   data()
string s1("hello world");
printf("%s\n", s1.c_str); // 转化为 const char * 类型字符串,以 '\0' 结尾

  字符串流处理,除了标准流和文件流输入输出外,还可以从 string 进行输入输出;类似 istream 和 ostream 进行标准流输入输出,我们用 istring 和 ostream 进行标准流输入输出,我们用 istringstream 和 ostringstream 进行字符串的输入和输出,也叫做内存输入输出。要包含头文件 #include < sstream >,举个例子,

#include <iostream>
#include <sstream>

using namespace std;

int main(){
    string input("Input test 123 4.7 A");
    istringstream inputString(input);  // 类实例化

    string str1, str2;
    int i; double d; char c;
    inputString >> str1 >> str2 >> i >> d >> c;
    cout << str1 << endl << str2 << endl;
    cout << i << endl << d << endl << c << endl;

    long L;
    if(inputString >> L)cout << "long\n";
    else cout << "empty\n";
}

结果如下
在这里插入图片描述
输出到屏幕方式如下,

ostringstream outputString;
int a = 10;
outputString << "This" << a << "ok" << endl;
cout << outputString.str();

输出为:
This 10ok

四、标准模板库 STL

  C++ 语言核心优势之一就是便于软件的重用
  C++ 中有两个方面体现重用:1. 面向对象的思想:继承和多态,标准类库;2. 泛型程序设计(generic programming) 的思想:模板机制,以及标准模板库 STL。
  泛型程序设计,将常用数据结构和算法写成模板,不论数据和数据结构放什么对象,算法针对什么对象,都不必实现数据结构,重新编写算法。
  标准模板库(Standard Template Library) 就是一些常用 数据结构和算法模板的集合。并且有非常高的性能。

4.1 简述

  基本概念

容器可以用来存放各种类型的数据(基本类型的变量,对象等)的数据结构,都是类模板
分为:
1) 顺序容器
vector 动态数组  deque 双向队列  list 双向链表
2) 关联容器
set multiset  map multimap // 都是排序的
2) 容器适配器
stack 栈 queque 队列 priority_queque 优先级队列

  对象被插入容器时,被插入的是对象的一个复制品。对容器的对象所属的类,往往还要重载 == 和 < 运算符等。

4.2 迭代器

  迭代器的基本属性,
----》用于指向顺序容器和关联容器中的元素
----》迭代器用法和指针类似
----》有 const 和非 const 两种
----》通过迭代器可以读取它指向的元素
----》通过非 const 迭代器还能修改其指向的元素
  定义一个容器类的迭代器方法可以是,

容器类名::iterator 变量名;
或者
容器类名::const::iterator 变量名;

访问一个迭代器指向的元素:
* 迭代器变量名

  举个例子,

#include <vector>
#include <iostream>

using namespace std;

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){
            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( j = v.begin(); j != v.end(); ++j){
            cout << *j << ","; // 迭代器看作指针
    }
    cout << endl;
}

结果如下,
在这里插入图片描述
双向迭代器
  若 p 和 p1 都是双向迭代器,则可对 p 和 p1 进行操作,

++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 < p1, p <= p1, p > p1, p >= p1

不同容器迭代器类型

容器             容器上的迭代器类别
vector          随机访问
deque           随机访问
list            双向
set/multiset    双向

stack           不支持迭代器
queue           不支持迭代器
priority_queue  不支持迭代器

有的算法,例如 sort, binary_search 需要通过随机访问迭代器来访问容器中的元素,那么 list 以及关联容器就不支持该算法。

vector(deque 也一样) 的迭代器是随机访问迭代器,遍历方法为

vector <int> v(100);
int i;
for(int 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 += 2;

list 的迭代器是双向迭代器,遍历方法为

list <int> v;
list <int>::const_iterator ii;
for(ii = v.begin(); ii != v.end(); ii++)cout << *ii;

// 错误方法
// 因为双向迭代器不支持 <, list 没有成员函数 []
for(ii = v.begin(); ii < v.end(); ii++)cout << *ii; 
for(ii = v.begin(); ii != v.end(); ii++)cout << v[i]; 
4.3 算法

  算法就是一个个函数模板,大多数在 < algorithm > 中定义
-----》STL 中提供了各种容器中通用的算法,比如查找,排序;
-----》算法通过迭代器来操纵容器中的元素。许多算法可以对容器中的一个局部区间进行操作,需要两个参数,一个是起始元素的迭代器,一个是终止元素的后面一个元素的迭代器。比如排序和查找;
-----》有的算法返回一个迭代器。比如 find() ,返回一个指向该元素的迭代器;
-----》算法可以处理容器,也可以处理数组。
  一些例子,

template <class Init, class T>
find(Init first, Init last, const & T); // 在[first, last)区间查找 T,返回迭代器

  在 STL 中关联容器内部的元素是 从小到大 排列的。使用 STL 时,在缺省情况下,这三种等价:x比y小,表达式"x<y"为真,y比x大。举个例子,STL中“相等”概念演示,

#include <iostream>
#include <algorithm>
using namespace std;

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)};
	cout << binary_search(a, a + 4, A(9)); // 折半查找

	return 0;
}

结果如下,
在这里插入图片描述
为什么最后结果是 1 ,因为与 STL 判断机制有关。比如 a > b || a < b 不成立,那么就认为 a = b。

4.4 vector, deque 和 list

vector 示例

#include <iostream>
#include <vector>

using namespace std;

template <class T>
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 装入容器
    cout << "1)" << v.end() - v.begin() << endl;

    // 两个随机迭代器可以相减
    cout << "2)"; printVector(v.begin(), v.end());

    v.insert(v.begin() + 2, 13); // 在数组 2 位置插入 13
    cout << "3)"; printVector(v.begin(), v.end());

    v.erase(v.begin() + 2); // 删除位于 begin() + 2 的元素
    cout << "4)"; printVector(v.begin(), v.end());

    vector <int> v2(4, 100); // v2 有 4 个元素,都是 100
    v2.insert(v2.begin(), v.begin() + 1, v.begin() + 3);
    // 将 v 的一段插入 v2 开头
    cout << "5) v2:"; printVector(v2.begin(), v2.end());

    v.erase(v.begin() + 1, v.begin() + 3);
    // 删除 v 上的一个区间,即 2, 3
    cout << "6)"; printVector(v.begin(), v.end());

    return 0;
}

在这里插入图片描述
vector 实现二维数组

#include <iostream>
#include <vector>

using namespace std;

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 **
  所有 vector 都适用于 deque 操作。deque 还有 push_front(将元素插入到前面) 和 pop_front (删除最前面的元素)操作,复杂度是 o(1).

**list 实例 **
  在任何位置插入删除都是常数时间,不支持随机存取。
  除了具有所有顺序容器都有的成员函数外,还有 8 个成员函数,

push_front: 在前面插入
pop_front:  删除前面元素
sort:       排序(list不支持STL的算法 sort)
remove:     删除和指定值相等的所有元素
unique:     删除所有和前一个元素相同的元素(要做到元素不重复,要先sort)
merge:      合并两个链表,并清除合并那个
reverse:    颠倒链表
splice:     在指定位置前面插入另一个链表中的一个或多个元素,并在另一链表中删除被插入元素

举个例子,

#include <iostream>
#include <list>
#include <algorithm>

using namespace std;

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){
    // 不推荐的写法,还是用两个迭代器作为参数更好
    typename list<T>::const_iterator i;
    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(2);
    lst1.push_back(3); lst1.push_back(4);
    lst1.push_back(2);
    lst2.push_back(10); lst2.push_back(20);
    lst2.push_back(30); lst2.push_back(30);
    lst2.push_back(30); lst2.push_back(40);
    lst2.push_back(10);

    cout << "1)"; printList(lst1); cout << endl;

    cout << "2)"; printList(lst2); cout << endl;

    lst2.sort();
    cout << "3)"; printList(lst2); cout << endl;

    lst2.pop_front();
    cout << "4)"; printList(lst2); cout << endl;

    lst1.remove(2);
    cout << "5)"; printList(lst1); cout << endl;

    lst2.unique();
    cout << "6)"; printList(lst2); cout << endl;

    lst1.merge(lst2);
    cout << "7)"; printList(lst1); cout << endl;
    cout << "8)"; printList(lst2); cout << endl;

    lst1.reverse();
    cout << "9)"; printList(lst1); cout << endl;

    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;

    cout << "11)"; printList(lst2); cout << endl;

    return 0;
}

输出结果为,
在这里插入图片描述

4.5 set 和 multiset

  关联容器
-----》内部元素有序排列,新元素插入的位置取决于它的值,查找速度快;
-----》除了各容器都有的函数外,还支持以下成员函数:

find:查找等于某个值(x 大于 y 和 y 小于 x 同时不成立立即为相等)
lower_bound:查找某个下界
upper_bound:查找某个上界
equal_bound:同时查找上界和下界
count:计算等于某个值的元素个数(~)
insert:用以插入一个元素或一个区间

  Pair 模板

template <class _T1, class _T2>
struct pair{
	typedef _T1 first_type;
	typedef _T2 second_type;
	_T1 first;
	_T2 second;
	pair():first(), second(){}
	pair(const _T1 & _a, const _T2 & _b):first(_a), second(_b){}

	template<class _U1, class _U2>
	pair(const pair <_U1, _U2> & _p):first(_p.first), second(_p.second){}
}

map/multimap 容器里放着的都是 pair 模板类的对象,且按 first 从小到大排列

第三个构造函数用法实例:
pair <int, int>
p(pair <double, double>(5.5, 4.6))
// p.first = 5, p.second = 4

  multiset 情况

template <class Key, class Pred = less < Key >,
	class A = allocator <Key> >
class multiset{......};
---> Pred 类型的变量决定 multiset 中元素,定义怎么比大小的。
     op(x, y) 返回值 true, 则 x 比 y 小. 缺省类型为 less < Key >
---> less 模板的定义:
template <class T>
struct less: public binary_function <T, T, bool>
{bool operator()(const T & x, const T & y){return x < y;}const;}
// less 模板靠 < 来比较大小

  multiset 的成员函数

// 在容器中查找值为 val 的元素,返回迭代器。找不到,返回 end()
iterator find(const T & val);

// 将 val 插入到容器中并返回迭代器
iterator insert(const T & val);

// 将区间 [first, last)插入容器
iterator insert(iterator first, iterator last);

// 统计有多少个元素的值和 val 相等
int count(const T & val);

// 查找一个最大的位置 it, 使得 [begin(), it) 中所有的元素都比 val 小
iterator lower_bound(const T & val);

// 查找一个最小的位置 it, 使得 [it, end()) 中所有的元素都比 val 大
iterator upper_bound(const T & val);

// 同时求得 lower_bound 和 upper_bound
pair <iterator, iterator> equal_range(const T & val);

// 删除 it 指向的元素,返回其后面的元素的迭代器(VS 这样的,dev c++ 不是)
iterator erase(iterator it);

  multiset 实际例子1

#include <iostream>
#include <set>
#include <algorithm>

using namespace std;

class A{};

int main(){
    // 等价于 multiset <A, less <A>> a;
    //   插入元素时,multiset 插入元素会和已有元素做比较。
    //   由于 less 模板是用 < 进行比较的,这样,要求 A 的
    //   对象能用 < 比较,即适当重载了 < .
    multiset <A> a;
    a.insert(A()); // error
}

  multiset 实际例子2

#include <iostream>
#include <set>
#include <algorithm>

using namespace std;

template <class T>
void Print(T first, T last){
    for(; first != last; ++first)
        cout << *first << " ";
}
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; // 用 < 号比大小
typedef multiset <A, myLess> 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);
    m1.insert(22);
    cout << "1)" << m1.count(22) << endl;

    cout << "2)"; Print(m1.begin(), m1.end());

    MSET1::iterator pp = m1.find(19);
    if(pp != m1.end()) // 右开区间
        cout << "found" << endl;
    // lower_bound 查找一个最大位置 it, 使得[begin, it)中所有元素都比 val 小
    cout << "3)"; cout << * m1.lower_bound(22) << ","
         << *m1.upper_bound(22) << endl;

    cout << "4)"; Print(m1.lower_bound(22), m1.upper_bound(22));
    cout << endl;

    cout << "5)"; cout << *pp << endl;

    MSET2 m2;
    m2.insert(a, a + SIZE);
    cout << "6)"; Print(m2.begin(), m2.end());

    return 0;
}

结果如下
在这里插入图片描述
  set 模板

template <class Key, class Pred = less <Key>,
		class A = allocator<Key>>
		class set{...}
插入 set 已有元素时,忽略插入

  set 例子

#include <iostream>
#include <set>
#include <algorithm>

using namespace std;

int main(){
    typedef set <int>::iterator IT;
    int a[5] = {3, 4, 6, 1, 2};
    set <int> st(a, a+5); // 这里是 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);
    cout << *bounds.first << "," << *bounds.second; // 输出 4, 5

    return 0;
}

结果如下,
在这里插入图片描述
Pair部分理解还不是很清楚。

4.6 map 和 multimap

  Pair 模板

template <class _T1, class _T2>
struct pair{
	typedef _T1 first_type;
	typedef _T2 second_type;
	_T1 first;
	_T2 second;
	pair():first(), second(){}
	pair(const _T1 & _a, const _T2 & _b):first(_a), second(_b){}

	template<class _U1, class _U2>
	pair(const pair <_U1, _U2> & _p):first(_p.first), second(_p.second){}
}

map/multimap 容器里放着的都是 pair 模板类的对象,且按 first 从小到大排列

第三个构造函数用法实例:
pair <int, int>
p(pair <double, double>(5.5, 4.6))
// p.first = 5, p.second = 4

  multimap模板

template <class Key, class T, class Pred = less <Key>,
		class A = allocator<T> >
class multimap{
	......
	typedef pair <const Key, T> value_type;
}; // Key 代表关键字类型
// 1. multimap 中的元素由 <关键字, 值> 组成,每个元素是一个 pair 对象,关键字就是 first 成员变量,类型是 Key
// 2. multimap 中允许多个元素的关键字相同。元素按照 first 成员变量从小到大排列
    缺省情况下用 less<Key>定义关键字小于关系

  multimap实例

#include <iostream>
#include <map>
#include <algorithm>

using namespace std;

int main(){
    typedef multimap <int, double, less<int> > mmid;
    mmid pairs;
    cout << "1)" << pairs.count(15) << endl;

    // typedef pair <const Key, T>value_type;
    pairs.insert(mmid::value_type(15, 2.7));
    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 << ")" << ",";
    }
}

输出结果为,
在这里插入图片描述
  map模板

template <class Key, class T, class Pred = less <Key>,
		class A = allocator<T> >
class multimap{
	......
	typedef pair <const Key, T> value_type;
}; 
// map 中的元素都是 pair 模板类对象。关键字 (first 成员变量) 各不相同。元素按照
// 关键字从小到大排列,缺省情况下用 less<Key>, 即“<”定义小于。

  map的[ ]成员函数

若 pairs 为 map 模板类的对象,
pairs[key]
返回关键字等于 key 的元素的值(second成员变量的引用)。若没有关键字为 key 的元素会往 pairs 里面插入
一个关键字为 key 的元素,其值用无参构造函数初始化,并返回其值引用。
如:
map <int, double> pairs;
pairs[50] = 5;

  map的示例

#include <iostream>
#include <map>
#include <algorithm>

using namespace std;

template <class Key, class Value>
ostream & operator <<(ostream & o, const pair<Key, Value> & p){
    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, 27));
    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)";
    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 << ",";

    return 0;
}

输出结果为,
在这里插入图片描述

4.7 容器适配器

  容器适配器中没有迭代器。下面开始介绍,
  stack相关例子

stack 是后进先出的数据结构,只能插入,删除,访问栈顶的元素。
可以进行以下操作:
push   插入元素
pop    弹出元素
top    返回栈顶元素引用,可以修改

  queue相关例子

queue 是先进先出的数据结构
可以进行以下操作:
push   插入元素,发生在对尾
pop    弹出元素,发生在对头
top    返回对头元素引用,可以修改,发生在对头
back   返回对尾元素的引用,也可以修改

  priority_queue相关例子

与 queue 类似,确保优先级最高的在对头,即保证最大元素在最前面,用堆排序技术实现
可以进行以下操作:
push   插入元素,发生在对尾 O(logn)
pop    删除的是最大的元素 O(logn)
top    返回最大元素引用 O(1)
back   返回对尾元素的引用,也可以修改

  举个例子,

#include <iostream>
#include <queue>
#include <algorithm>

using namespace std;

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
    cout << endl;

    return 0;
}

stack, queue, priority_queue 都有以下

empty()   成员函数用于判断适配器是否为空
size()    成员函数返回适配器中元素个数

五、总结

  还有一些数据结构部分没整理完,最近把这篇博客补充完整,但是 C++ 知识学习完成了。现在对算法,计算机网络,操作系统进行系统学习了,博客也会记录。C++ 学习部分完成,以后会更深入。

  • 2
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值