C++提高编程 上

模板

模板的概念

模板就是建立通用的模具,大大提高复用性

模板的特点:

  • 模板不可以直接使用,它只是一个框架
  • 模板的通用并不是万能的

函数模板

  • C++ 另一种编程思想称为泛型编程,主要利用的技术就是模板
  • C++ 提供两种模板机制:函数模板类模板

函数模板语法

函数模板作用:
  建立一个通用函数,其函数返回值类型和形参类型可以不具体制定,用一个虚拟的类型来表示

语法

template<typename T>
函数声明或定义

解释
template ——声明创建模板
typename ——表明其后面的符号是一种数据类型,可以用class代替
T ——通用的数据类型,名称可以替换,通常为大写字母

示例

#include <iostream>
using namespace std;

template<typename T>
void mySwap(T& a, T& b) {
	T temp = a;
	a = b;
	b = temp;
}

void test01() {
	int a = 10;
	int b = 20;

	//1.自动类型推导
	mySwap(a, b);
	cout << a << endl;
	cout << b << endl;

	//2.显式指定参数
	mySwap<int>(a, b);
	cout << a << endl;
	cout << b << endl;
}

int main() {
	test01();

	system("pause");
	return 0;
}

总结:

  • 函数模板利用关键字 template
  • 使用函数模板有两种方式:自动类型推导、显式指定类型
  • 模板的目的是为了提高复用性,将类型参数化

函数模板注意事项

注意事项:

  • 自动类型推导,必须推导出一致的数据类型T才可以使用
  • 模板必须要确定出T的数据类型才可以使用

示例

#include <iostream>
using namespace std;

template<typename T>
void mySwap(T& a, T& b) {
	T temp = a;
	a = b;
	b = temp;
}

//1.自动类型推导,必须推导出一致的数据类型T才可以使用
void test01() {
	int a = 10;
	int b = 20;
	char c = 'c';
	mySwap(a, b);

	/*mySwap(a, c);*/ //错误,推导不出一致的T类型
}

template<typename T>
void func() {
	cout << "模板调用" << endl;
}

int main() {
	//2.模板必须要确定出T的数据类型才可以使用
	func<int>();

	system("pause");
	return 0;
}

总结

  • 使用模板时必须确定出通用数据T,并且能够推导出一致的类型

函数模板案例

案例描述:

  • 利用函数模板封装一个排序的函数,可以对不同数据类型数组进行排序
  • 排序规则从大到小,排序算法为选择排序
  • 分别利用char数组int数组进行测试

示例

#include <iostream>
using namespace std;

template<typename T>
void mySwap(T& a, T& b) {
	T temp = a;
	a = b;
	b = temp;
}

template<typename T>
void mySort(T& arr, int len) {
	for (int i = 0; i < len; i++) {
		int max = i;
		for (int j = i + 1; j < len; j++) {
			if (arr[max] < arr[j]) {
				max = j;

			}
		}
		if (max != i) {
			mySwap(arr[max], arr[i]);
		}
	}
}

template<typename T>
void printArr(T arr,int len) {
	for (int i = 0; i < len; i++) {
		cout << arr[i] << "\t";
	}
}

void test01() {
	char charArr[] = "zpxiocu";
	int len = sizeof(charArr) / sizeof(char);
	mySort(charArr, len);
	printArr(charArr, len);
}

void test02() {
	int intArr[] = { 5,1,4,7,9,2 };
	int len = sizeof(intArr) / sizeof(int);
	mySort(intArr, len);
	printArr(intArr, len);
}

int main() {
	test01();
	test02();

	system("pause");
	return 0;
}

模板函数和普通函数的区别

普通函数与函数模板的区别

  • 普通函数调用时可以发生自动类型转换(隐式类型转换)
  • 函数模板调用时,如果利用自动类型推导,不会发生隐式转换
  • 如果利用显示指定类型的方式,可以发生隐式类型转换

示例

#include <iostream>
using namespace std;

int myAdd01(int a, int b) {
	return a + b;
}

template<typename T>
T myAdd02(T a, T b) {
	return a + b;
}

void test01() {
	int a = 10;
	int b = 20;
	char c = 'c';

	cout << myAdd01(a, c) << endl;

	cout << myAdd02(a, b) << endl;
	
	//报错,自动类型推导不会发生隐式类型转换
	// cout << myAdd02(a, c) << endl;

	//显示指定类型不会报错
	cout << myAdd02<int>(a, c) << endl;
}

int main() {
	test01();

	system("pause");
	return 0;
}

普通函数与函数模板的调用规则

调用规则如下:

  1. 如果函数模板和普通函数都可以实现,优先调用普通函数
  2. 如果可以通过空模板参数列表来强制调用函数模板
  3. 函数模板也可以发生重载
  4. 如果函数模板可以产生更好的匹配,优先调用函数模板

示例

#include <iostream>
using namespace std;

void myPrint(int a, int b) {
	cout << "调用的普通函数" << endl;
}

template<typename T>
void myPrint(T a, T b) {
	cout << "调用的函数模板" << endl;
}

template<typename T>
void myPrint(T a, T b, T c) {
	cout << "调用的c函数模板" << endl;
}

void test01() {
	int a = 10;
	int b = 20;
	//调用的普通函数
	myPrint(a, b);

	//通过空模板参数列表,强制调用函数模板
	myPrint<>(a, b);

	int c = 1;
	myPrint(a, b, c);

	char d = 'd';
	char e = 'e';
	myPrint(e, d);
}

int main() {
	test01();

	system("pause");
	return 0;
}

总结:既然提供了函数模板,最好就不要提供普通函数,否则容易出现二义性

模板的局限性

局限性

  • 模板的通用性并不是万能的

例如:

template<class T>
void f(T a, T b){
	a = b;
}

在上述代码中提供的赋值操作,如果传入的a和b是一个数组,就无法实现了

在例如:

template<class T>
void f(T a, T b){
	if(a > b){ ... }
}

在上述代码中,如果T的数据类型传入的是像Person这样的自定义数据类型,也无法正常运行

因此C++为了解决这种问题,提供模板的重载,可以为这些特定的类型提供具体化的模板

示例

#include <iostream>
using namespace std;

class Person {
public:
	Person(string name, int age) {
		this->name = name;
		this->age = age;
	}
	string name;
	int age;
};

template<class T>
bool myCompare(T& a, T& b) {
	if (a == b) {
		return true;
	}
	else {
		return false;
	}
}

//利用具体化Person的版本来实现代码,具体化优先调用
template<> bool myCompare(Person& p1, Person& p2) {
	if (p1.age == p2.age && p1.name == p2.name) {
		return true;
	}
	else {
		return false;
	}
}

void test01() {
	int a = 10;
	int b = 10;
	bool res = myCompare(a, b);
	cout << res << endl;
}

void test02() {
	Person p1 = { "Tom",10 };
	Person p2 = { "Tom",10 };

	bool res = myCompare(p1, p2);
	cout << res << endl;
}

int main() {
	test01();
	test02();

	return 0;
}

总结:

  • 利用具体化的模板,可以解决自定义类型的通用化
  • 学习模板并不是为了写模板,而是在STL能够运用系统提供的模板

类模板

类模板语法

类模板作用:

  • 建立一个通用类,类中的成员 数据类型可以不具体指定,用一个虚拟的类型来代表

语法

template<typename T>

解释
template — 声明创建模板
typename — 表明其后面的符号是一种数据类型,可以用class代替
T — 通用的数据类型,名称可以替换,通常为大写字母

示例

#include<iostream>
using namespace std;

template<class NameType,class AgeType>
class Person {
public:
	Person(NameType name, AgeType age) {
		this->name = name;
		this->age = age;
	}

	void showPerson() {
		cout << this->name << endl;
		cout << this->age << endl;
	}

	NameType name;
	AgeType age;
};

void test01() {
	Person<string, int> p1("孙悟空", 99);
	p1.showPerson();
}

int main() {
	test01();

	system("pause");
	return 0;
}

总结:类模板和函数模板语法相似,在声明模板template后面加类,此类称为类模板

类模板与函数模板区别

类模板与函数模板区别主要有两点:

  1. 类模板没有自动类型推导的使用方式
  2. 类模板在模板参数列表中可以有默认参数

示例

#include<iostream>
using namespace std;

template<class NameType,class AgeType = int>
class Person {
public:
	Person(NameType name, AgeType age) {
		this->name = name;
		this->age = age;
	}

	void showPerson() {
		cout << this->name << endl;
		cout << this->age << endl;
	}

	NameType name;
	AgeType age;
};

void test01() {
	//报错,类模板没有自动类型推导
	//Person p("孙悟空", 99);

	Person<string, int> p("孙悟空", 99);
	p.showPerson();
}

void test02() {
	Person<string> p("猪八戒", 100);
	p.showPerson();
}

int main() {
	test01();
	test02();

	system("pause");
	return 0;
}

类模板中成员函数创建时机

类模板中成员函数和普通类中成员函数创建时机是有区别的:

  • 普通类中的成员函数一开始就可以创建
  • 类模板中的成员函数在调用时才创建

示例

#include<iostream>
using namespace std;

class Person1 {
public:
	void showPerson1() {
		cout << "Person1" << endl;
	}
};

class Person2 {
public:
	void showPerson2() {
		cout << "Person2" << endl;
	}
};

template<class T>
class MyClass {
public:
	T obj;

	void func1() {
		obj.showPerson1();
	}

	void func2() {
		obj.showPerson2();
	}
};

void test01() {
	MyClass<Person1> p1;
	p1.func1();
	//报错,函数调用才会去创建成员函数
	//p1.func2();
}

int main() {
	test01();

	system("pause");
	return 0;
}

总结:类模板中的成员函数并不是一开始就创建的,在调用时才去创建

类模板对象做函数参数

一共有三种传入方式:

  1. 指定传入的类型 — 直接显示对象的数据类型
  2. 参数模板化 — 将对象中的参数变为模板进行传递
  3. 整个类模板化 — 将这个对象类型模板化进行传递

示例

#include<iostream>
using namespace std;

template<class T1,class T2>
class Person {
public:
	Person(T1 name, T2 age) {
		this->name = name;
		this->age = age;
	}

	void showPerson() {
		cout << this->name << endl;
		cout << this->age << endl;
	}

	T1 name;
	T2 age;
};

//1.指定传入类型
void printPerson1(Person<string, int>& p) {
	p.showPerson();
}

void test01() {
	Person<string, int> p("孙悟空", 99);
	printPerson1(p);
}

//2.参数模板化
template<class T1,class T2>
void printPerson2(Person<T1, T2>& p) {
	p.showPerson();
	cout << "T1类型为" << typeid(T1).name() << endl;
	cout << "T2类型为" << typeid(T2).name() << endl;
}

void test02() {
	Person<string, int> p("猪八戒", 98);
	printPerson2(p);
}

//3.整个类模板化
template<class T>
void printPerson3(T& p) {
	p.showPerson();
}

void test03() {
	Person<string, int> p("唐僧", 30);
	printPerson3(p);
}

int main() {
	test01();
	test02();
	test03();

	system("pause");
	return 0;
}

总结:

  • 通过类模板创建的对象,可以有三种方式向函数中进行传参
  • 使用比较广泛是第一种:指定传入的类型

类模板与继承

当类模板碰到继承时,需要注意以下几点:

  • 当子类继承的父类是一个类模板时,子类在声明的时候,要指定出父类中T的类型
  • 如果不指定,编译器无法给子类分配内存
  • 如果想灵活指定出父类中T的类型,子类也需变为类模板

示例

#include<iostream>
using namespace std;

template<class T>
class Base {
	T m;
};


//class Son : public Base 错误,必须要知道父类中的T类型,才能继承给子类
class Son :public Base<int> {

};

void test01() {
	Son s1;
}

//如果想灵活指定父类中T类型,子类也需要变类模板
template<class T1,class T2>
class Son2 :public Base<T1> {
	T2 obj;
};

void test02() {
	Son2<int, char> s2;
}

int main() {


	system("pause");
	return 0;
}

总结:如果父类是类模板,子类需要指定出父类中T的数据类型

类模板成员函数类外实现

示例

#include<iostream>
using namespace std;

template<class T1,class T2>
class Person {
public:
	Person(T1 name, T2 age);
	void showPerson();

	T1 name;
	T2 age;
};

template<class T1,class T2>
Person<T1, T2>::Person(T1 name, T2 age) {
	this->age = age;
	this->name = name;
}

template<class T1,class T2>
void Person<T1,T2>::showPerson() {
	cout << this->name << endl;
	cout << this->age << endl;
}

void test01() {
	Person<string, int> p("Tom", 15);
	p.showPerson();
}

int main() {
	test01();

	return 0;
}

类模板分文件编写

问题:

  • 类模板中成员函数创建时机实在调用阶段,导致分文件编写时链接不到

解决:

  • 解决方式1:直接包含.cpp源文件
  • 解决方式2:将声明和实现写到同一个文件中,并更改后缀为.hpp,hpp是约定的词缀,不是强制

示例
person.hpp中代码

#include<iostream>
using namespace std;

template<class T1,class T2>
class Person {
public:
	Person(T1 name, T2 age);
	void showPerson();

	T1 name;
	T2 age;
};

template<class T1,class T2>
Person<T1, T2>::Person(T1 name, T2 age) {
	this->name = name;
	this->age = age;
}

template<class T1,class T2>
void Person<T1, T2>::showPerson() {
	cout << this->name << endl;
	cout << this->age << endl;
}

主文件.cpp中的代码

#include<iostream>
using namespace std;
#include "person.hpp"

void test01() {
	Person<string, int> p("Tom", 15);
	p.showPerson();
}

int main() {
	test01();

	return 0;
}

类模板与友元

全局函数类内实现——直接在类内声明友元即可
全局函数类外实现——需要提前让编译器知道全局函数的存在

示例

#include<iostream>
using namespace std;

//需要让编译器提前知道这个函数和相关内容的存在
template<class T1,class T2>
class Person;

template<class T1, class T2>
void printPerson2(Person<T1, T2>p) {
	cout << p.name << endl;
	cout << p.age << endl;
}

template<class T1,class T2>
class Person {
	friend void printPerson(Person<T1, T2> p) {
		cout << p.name << endl;
		cout << p.age << endl;
	}

	//全局函数类外实现
	//加空模板参数列表
	//如果全局函数时类外实现,需要让编译器提前知道这个函数的存在
	friend void printPerson2<>(Person<T1, T2>p);

public:
	Person(T1 name, T2 age) {
		this->name = name;
		this->age = age;
	}

private:
	T1 name;
	T2 age;
};

void test01() {
	Person<string, int> p("Tom", 15);
	printPerson(p);
}

void test02() {
	Person<string, int> p("Jay", 155);
	printPerson2(p);
}

int main() {
	test01();
	test02();

	return 0;
}

类模板案例

  案例描述:实现一个通用的数组类,要求如下:

  • 可以对内置数据类型以及自定义数据类型的数据进行存储
  • 将数组中的数据存储到堆区
  • 构造函数中可以传入数组的容量
  • 提供对应的拷贝构造函数以及operater=防止浅拷贝问题
  • 提供尾插法和尾删法对数组中的数据进行增加和删除
  • 可以通过下标的方式访问数组中的元素
  • 可以获取数组中当前元素个数和数组的容量

示例
MyArray.hpp中代码:

#include<iostream>
using namespace std;
#pragma once

template<class T>
class MyArray {
public:
	MyArray(int capacity) {
		this->m_Capacity = capacity;
		this->m_Size = 0;
		this->pAddress = new T[this->m_Capacity];
	}

	MyArray(const MyArray& arr) {
		this->m_Capacity = arr.m_Capacity;
		this->m_Size = arr.m_Size;
		this->pAddress = new T[this->m_Capacity];

		for (int i = 0; i < this->m_Size; i++) {
			this->pAddress[i] = arr.pAddress[i];
		}
	}

	MyArray& operator=(const MyArray& arr) {
		if (this->pAddress != NULL) {
			delete[] this->pAddress;
			this->pAddress = NULL;
			this->m_Capacity = 0;
			this->m_Size = 0;
		}

		this->m_Capacity = arr.m_Capacity;
		this->m_Size = arr.m_Size;
		this->pAddress = new T[this->m_Capacity];
		for (int i = 0; i < this->m_Capacity; i++) {
			this->pAddress[i] = arr.pAddress[i];
		}

		return *this;
	}

	void Push_Back(const T& val) {
		if (this->m_Capacity == this->m_Size) {
			return;
		}
		this->pAddress[this->m_Size] = val;
		this->m_Size++;
	}

	void Pop_Back() {
		if (this->m_Size == 0) {
			return;
		}
		this->m_Size--;
	}

	T& operator[](int index) {
		return this->pAddress[index];
	}

	int getCapacity() {
		return this->m_Capacity;
	}

	int getSize() {
		return this->m_Size;
	}

	~MyArray() {
		if (this->pAddress != NULL) {
			delete[] this->pAddress;
			this->pAddress = NULL;
		}
	}

private:
	T* pAddress;
	int m_Capacity;
	int m_Size;
};

main.cpp中代码:

#include<iostream>
using namespace std;
#include "MyArray.hpp"

void printArray(MyArray<int>& arr) {
	for (int i = 0; i < arr.getSize(); i++) {
		cout << arr[i] << endl;
	}
}

void test01() {
	MyArray<int> arr(5);
	for (int i = 0; i < 5; i++) {
		arr.Push_Back(i);
	}
	printArray(arr);

	cout << arr.getCapacity() << endl;
	cout << arr.getSize() << endl;

	MyArray<int> arr1(arr);
	arr1.Pop_Back();
	printArray(arr1);
}

class Person {
public:
	Person() {};
	Person(string name, int age) {
		this->name = name;
		this->age = age;
	}

	string name;
	int age;
};

void printPersonArray(MyArray<Person>& arr) {
	for (int i = 0; i < arr.getSize(); i++) {
		cout << arr[i].name << endl;
		cout << arr[i].age << endl;
	}
}

void test02() {
	MyArray<Person> arr(10);
	Person p1("孙悟空", 99);
	Person p2("孙悟饭", 9);
	Person p3("孙悟天", 91);
	Person p4("孙悟1", 92);
	Person p5("孙悟2", 93);

	arr.Push_Back(p1);
	arr.Push_Back(p2);
	arr.Push_Back(p3);
	arr.Push_Back(p4);
	arr.Push_Back(p5);

	printPersonArray(arr);
	cout << arr.getCapacity() << endl;
	cout << arr.getSize() << endl;
}

int main() {
	test01();
	test02();

	system("pause");
	return 0;
}

STL初识

STL的诞生

  • 长久以来,软件界一直希望建立一种可重复利用的东西
  • C++的面向对象泛型编程思想,目的就是复用性的提升
  • 大多情况下,数据结构和算法都未能有一套标准,导致被迫从事大量重复工作
  • 为了建立数据结构和算法的一套标准,诞生了STL

STL基本概念

  • STL(标准模板库
  • STL从广义上分为:容器、算法、迭代器
  • 容器算法之间通过迭代器进行无缝连接
  • STL几乎所有的代码都采用了模板类或者模板函数

STL六大组件

STL大体分为六大组件,分别是:容器、算法、迭代器、仿函数、适配器(配接器)、空间配置器

  1. 容器:各种数据结构
  2. 算法:各种常用的算法
  3. 迭代器:扮演了容器与算法之间的胶合剂
  4. 仿函数:行为类似函数,可作为算法的某种策略
  5. 适配器:一种用来修饰容器或者仿函数或迭代器接口的东西
  6. 空间配置器:负责空间的配置与管理

STL中容器、算法、迭代器

容器就是将运用最广泛的一些数据结构实现出来

常用的数据结构:数组、链表、栈、树、队列、集合、映射表 等

这些容器分为序列式容器关联式容器两种:

  序列式容器:强调值的排序,序列容器中的每个元素均有固定的位置

  关联式容器:二叉树结构,各元素之间没有严格的物理上的顺序关系

算法:有限的步骤,解决逻辑或数学上的问题,这一门学科叫做算法

算法分为质变算法非质变算法

  质变算法是指运算过程中会更改区间内的元素的内容

  非质变算法是指运算过程中不会更改区间内的元素内容

迭代器是容器和算法之间的粘合剂

提供一种方法,使之能够依序寻访某个容器所含的各个元素,而又无需暴露该容器的内部表示方式

每个容器都有自己专属的迭代器

迭代器使用非常类似于指针

迭代器种类:

种类功能支持运算
输入迭代器对数据的只读访问只读,支持++、==、!=
输出迭代器对数据的只写访问只写,支持++
前向迭代器读写操作,并能向前推进迭代器读写,支持++、==、!=
双向迭代器读写操作,并能向前和向后操作读写,支持++、- -
随机访问迭代器读写操作,可以以跳跃的方式访问任意数据,功能最强的迭代器读写,支持++、- -、[n]、-n、<、<=、>、>=

常用的容器中迭代器种类尾双向迭代器和随机访问迭代器

容器算法迭代器初识

STL中最常用的容器为Vector,可以理解为数组

vector存放内置数据类型

容器:vector
算法:for_each
迭代器:vector<int>::iterator

示例

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

void myPrint(int val) {
	cout << val << endl;
}

void test01() {
	vector<int> v;
	//像容器中插入数据,尾插法
	v.push_back(10);
	v.push_back(20);
	v.push_back(30);
	v.push_back(40);
	//第一种遍历方式
	//起始迭代器,指向容器中第一个元素
	vector<int>::iterator itBegin = v.begin();
	//结束迭代器,指向容器中最后一个元素的下一个位置
	vector<int>::iterator itEnd = v.end();
	while (itBegin != itEnd) {
		cout << *itBegin << endl;
		itBegin++;
	}

	//第二种遍历方式
	for (vector<int>::iterator it = v.begin(); it != v.end(); it++) {
		cout << *it << endl;
	}

	//第三种遍历方式 利用STL遍历算法
	for_each(v.begin(), v.end(), myPrint);
}

int main() {
	test01();

	return 0;
}

vector存放自定义数据类型

示例

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

class Person {
public:
	Person(string name, int age) {
		this->name = name;
		this->age = age;
	}

	string name;
	int age;
};

void test01() {
	vector<Person> v;
	Person p1("a", 10);
	Person p2("b", 20);
	Person p3("c", 30);
	Person p4("d", 40);
	Person p5("e", 60);
	v.push_back(p1);
	v.push_back(p2);
	v.push_back(p3);
	v.push_back(p4);
	v.push_back(p5);

	for (vector<Person>::iterator it = v.begin(); it != v.end(); it++) {
		cout << it->name << endl;
		cout << it->age << endl;
	}
}

//存放自定义数据类型指针
void test02() {
	vector<Person*> v;
	Person p1("a", 10);
	Person p2("b", 20);
	Person p3("c", 30);
	Person p4("d", 40);
	Person p5("e", 60);
	v.push_back(&p1);
	v.push_back(&p2);
	v.push_back(&p3);
	v.push_back(&p4);
	v.push_back(&p5);

	for (vector<Person*>::iterator it = v.begin(); it != v.end(); it++) {
		cout << (*it)->name << endl;
		cout << (*it)->age << endl;
	}
}

int main() {
	test01();
	test02();

	return 0;
}

Vector容器嵌套容器

示例

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

void test01() {
	vector<vector<int>> v;

	vector<int> v1;
	vector<int> v2;
	vector<int> v3;
	vector<int> v4;

	for (int i = 0; i < 4; i++) {
		v1.push_back(i);
		v2.push_back(i + 1);
		v3.push_back(i + 2);
		v4.push_back(i + 4);
	}

	v.push_back(v1);
	v.push_back(v2);
	v.push_back(v3);
	v.push_back(v4);

	for (vector<vector<int>>::iterator it = v.begin(); it != v.end(); it++) {
		for (vector<int>::iterator vit = (*it).begin(); vit != (*it).end(); vit++) {
			cout << *vit;
		}
		cout << endl;
	}
}

int main() {
	test01();

	return 0;
}

STL-常用容器

string 容器

string基本概念

本质
string是C++风格的字符串,而string本质上是一个类

string和char*的区别:

  • char*是一个指针
  • string是一个类,类内部封装了char*,管理这个字符串,是一个char*型的容器

特点
string类内部封装了很多成员方法
string管理char*所分配的内存,不用担心复制越界和取值越界等,由类内部进行负责

string构造函数

构造函数原型:

  • string(); //创建一个空的字符串 例如:string str;
    string(const char\* s); //使用字符串s初始化
  • string(const string& str); //使用一个string对象初始化另一个string对象
  • string(int n,char c); //使用n个字符初始化

示例

#include<iostream>
using namespace std;

void test01() {
	string s1;

	const char* str = "helloWorld";
	string s2(str);

	cout << s2 << endl;

	string s3(s2);
	cout << s3 << endl;

	string s4(10, 'a');
	cout << s4 << endl;
}

int main() {
	test01();

	return 0;
}

总结:string的多种构造方法没有可比性,灵活使用即可

string赋值操作

功能描述

  • 给string字符串进行赋值

赋值的函数原型:

  • string& operator=(const char* s); //char* 类型字符串,赋值给当前的字符串
  • string& operator=(const string &s); //把字符串s赋给当前的字符串
  • string& operator=(char c); //字符赋值给当前的字符串
  • string& assign(const char *s); //把字符串s赋给当前的字符串
  • string& assign(const char *s,int n); //把字符串s的前n个字符赋给当前的字符串
  • string& assign(const string &s; //把字符串s赋给当前字符串
  • string& assign(int n,char c); //把n个字符c赋给当前字符串

示例

#include<iostream>
using namespace std;

void test01() {
	string str1;
	str1 = "hello";
	cout << str1 << endl;

	string str2;
	str2 = str1;
	cout << str2 << endl;

	string str3;
	str3 = 'a';
	cout << str3 << endl;

	string str4;
	str4.assign("world");
	cout << str4 << endl;

	string str5;
	str5.assign("world", 3);
	cout << str5 << endl;

	string str6;
	str6.assign(str5);
	cout << str6 << endl;

	string str7;
	str7.assign(10, 'a');
	cout << str7 << endl;
}

int main() {
	test01();

	return 0;
}

总结:
  string的赋值方式很多,operator=这种方式是比较实用的

string字符串拼接

功能描述
  实现在字符串尾拼接字符串

函数原型

  • string& operator+=(const char* str); //重载+=操作符
  • string& operator+=(const char c); //重载+=操作符
  • string& operator+=(const string& str); //重载+=操作符
  • string& append(const char *s); //把字符串s连接到当前字符串结尾
  • string& append(const char *s,int n); //把字符串s的前n个字符连接到当前字符串结尾
  • string& append(const string &s); //同operator+=(const string& str)
  • string& append(const string &s,int pos,int n); //字符串s中从pos开始的n个字符连接到字符串结尾

示例

#include<iostream>
using namespace std;

void test01() {
	string str1 = "I";
	str1 += " like";

	cout << str1 << endl;

	str1 += ':';
	cout << str1 << endl;

	string str2 = "this";
	str1 += str2;
	cout << str1 << endl;

	str1.append(" game");
	cout << str1 << endl;

	str1.append(" just1", 5);
	cout << str1 << endl;

	str2 = " like";
	str1.append(str2);
	cout << str1 << endl;

	string str3 = " you111";
	str1.append(str3, 0, 4);
	cout << str1 << endl;
}

int main() {
	test01();

	return 0;
}

string查找和替换

功能描述
  查找:查找指定字符串是否存在
  替换:在指定的位置替换字符串

函数原型

  • int find(const string& str,int pos = 0) const; //查找str第一次出现位置,从pos开始查找
  • int find(const char* s,int pos = 0) const; //查找s第一次出现位置,从pos开始查找
  • int find(const char*s,int pos,int n) const; //从pos位置查找s的前n个字符第一次位置
  • in find(const char c,int pos = 0) const; //查找字符c第一次出现位置
  • int rfind(const string& str,int pos = npos) const; //查找str最后一次位置,从pos开始查找
  • int rfind(const char* s,int pos = npos) const; //查找s最后一次出现位置,从pos开始查找
  • int rfind(const char*s,int pos,int n) const; //从pos查找s的前n个字符最后一次位置
  • int rfind(const char c,int pos = 0) const; //查找字符c最后一次位置
  • string& replace(int pos,int n,const string& str); //替换从pos开始n个字符为字符串str
  • string& replace(int pos,int n,const char* s); //替换从pos开始的n个字符为字符串s

示例

#include<iostream>
using namespace std;

void test01() {
	string str1 = "abcdefgde";
	int res = str1.find("de");
	if (res == -1) {
		cout << "未找到字符串" << endl;
	}
	cout << res << endl;

	//rfind是从右往左查找,find是从左往右查找
	res = str1.rfind("de");
	cout << res << endl;
}

void test02() {
	string str1 = "abcdefg";
	str1.replace(1, 3, "1111");
	cout << str1 << endl;
}

int main() {
	test01();
	test02();

	return 0;
}

string字符串比较

功能描述
  字符串之间的比较

比较方式
  字符串是按字符的ASCII码进行对比

= 返回0
> 返回1
< 返回-1

函数原型

  • int compare(const string &s) const; //与字符串s比较
  • int compare(const char *s) const; //与字符串s比较

示例

#include <iostream>
using namespace std;

void test01() {
	string str1 = "hello";
	string str2 = "hello";

	if (str1.compare(str2) == 0) {
		cout << "=" << endl;
	}
	str1 = "xello";
	if (str1.compare(str2) > 0) {
		cout << ">" << endl;
	}
	str2 = "yello";
	if (str1.compare(str2) < 0) {
		cout << "<" << endl;
	}
}

int main() {
	test01();

	return 0;
}

string字符存取

string中单个字符存取方式有两种
  char& operator[](int n) //通过[]方式取字符
  chhar& at(int n) //通过at方法获取字符

示例

#include<iostream>
using namespace std;

void test01() {
	string str = "hello";

	for (int i = 0; i < str.size(); i++) {
		cout << str[i] << "\t";
	}
	cout << endl;
	for (int i = 0; i < str.size(); i++) {
		cout << str.at(i) << "\t";
	}
	cout << endl;

	str[0] = 'x';
	cout << str << endl;
	str.at(1) = 'x';
	cout << str << endl;
}

int main() {
	test01();

	return 0;
}

string插入和删除

功能描述
  对string字符串进行插入和删除字符操作
函数原型
  string& insert(int pos, const char* s); //插入字符串
  string& insert(int pos,const string& str); //插入字符串
  string& insert(int pos,int n,char c); //在指定位置插入n个字符c
  string&& erase(int pos,int n=npos); //删除从Pos开始的n个字符

示例

#include<iostream>
using namespace std;

void test01() {
	string str = "hello";

	str.insert(1, "111");
	cout << str << endl;

	str.erase(1, 3);
	cout << str << endl;
}

int main() {
	test01();

	return 0;
}

string子串

功能描述
  从字符串中获取想要的子串
函数原型
  string substr(int pos=0,int n=npos) const; //返回由pos开始的n个字符组成的字符串

示例

#include<iostream>
using namespace std;

void test01() {
	string str = "abcdef";
	string subStr = str.substr(1, 3);
	cout << subStr << endl;
}

void test02() {
	string email = "hello@sina.com";

	int pos = email.find("@");

	string userName = email.substr(0, pos);
	cout << userName << endl;
}

int main() {
	test01();
	test02();

	return 0;
}

vector 容器

vector 基本概念

功能
  vector数据结构和数组非常相似,也称为单端数组
vector与普通数组区别
  不同之处在于数组是形态空间,而vector可以动态拓展
动态拓展
  并不是在原空间之后续接新空间,而是找更大的内存空间,然后将原数据拷贝新空间,释放原空间

vector容器的迭代器是支持随机访问的迭代器

vector 构造函数

功能描述
  创建vector容器
函数原型
  vector<T> v; //采用模板实现类实现,默认构造函数
  vector(v.begin(),v.end()); //将v[begin(),end())区间中的元素拷贝给本身
  vector(n,elem); //构造函数将n个elem拷贝给本身
  vector(const vector &vec); //拷贝构造函数

示例

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

void printVector(vector<int>& v) {
	for (vector<int>::iterator it = v.begin(); it != v.end(); it++) {
		cout << *it << endl;
	}
}

void test01() {
	//默认构造函数
	vector<int> v1;
	for (int i = 0; i < 10; i++) {
		v1.push_back(i);
	}
	printVector(v1);

	//通过区间方式进行构造
	vector<int>v2(v1.begin(), v1.end());
	printVector(v2);

	//n个elem方式构造
	vector<int>v3(10, 100);
	printVector(v3);

	//拷贝构造
	vector<int>v4(v3);
	printVector(v4);
}

int main() {
	test01();

	return 0;
}

vector赋值操作

功能描述
  给vector容器进行赋值
函数原型
  vector& operator=(const vector &vec); //重载等号操作符
  assign(beg,end); //将[beg,end)区间中的数据拷贝赋值给本身
  assign(n,elem); //将n个elem拷贝赋值给本身

示例

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

void printVector(vector<int>& v) {
	for (vector<int>::iterator it = v.begin(); it != v.end(); it++) {
		cout << *it << " ";
	}
	cout << endl;
}

void test01() {
	vector<int>v1;
	for (int i = 0; i < 10; i++) {
		v1.push_back(i);
	}
	printVector(v1);

	//赋值 operator=
	vector<int> v2;
	v2 = v1;
	printVector(v2);

	//assign
	vector<int>v3;
	v3.assign(v1.begin(), v1.end());
	printVector(v3);

	vector<int>v4;
	v4.assign(10, 100);
	printVector(v4);
}

int main() {
	test01();

	return 0;
}

vector 容量和大小

功能描述
  对vector容量的容量和大小操作
函数原型
  empty(); //判断容器是否为空
  capacity(); //容器的容量
  size(); //返回容器中元素的个数
  resize(int num); //重新指定容器的长度为num,若容器变长,则以默认值填充新位置。如果容器变短,则末尾超出容器长度的元素被删除
  resize(int num.elem); //重新指定容器的长度为num,若容器变长,则以elem值填充新位置。如果容器变短,则末尾超出容器长度的元素被删除

示例

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

void printVector(vector<int>v) {
	for (vector<int>::iterator it = v.begin(); it != v.end(); it++) {
		cout << *it << "\t";
	}
	cout << endl;
}

void test01() {
	vector<int> v1;
	for (int i = 0; i < 10; i++) {
		v1.push_back(i);
	}
	printVector(v1);

	if (v1.empty()) {
		cout << "empty" << endl;
	}
	
	cout << v1.capacity() << endl;
	cout << v1.size() << endl;
	v1.resize(15,100);
	printVector(v1);

	v1.resize(5);
	printVector(v1);
}

int main() {
	test01();

	return 0;
}

vector插入和删除

功能描述

  对vector容器进行插入、删除操作

函数原型

  • push_back(ele); //尾部插入元素ele
  • pop_back(); //删除最后一个元素
  • insert(const_iterator pos,ele); //迭代器指向位置pos插入元素ele
  • insert(const_iterator pos,int count,ele); //迭代器指向位置pos插入count个元素ele
  • erase(const_iterator pos); //删除迭代器指向的位置
  • erase(const_iterator start,const_iterator end); //删除迭代器从start到end之间的元素
  • clear(); //删除容器中所有元素

示例

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

void printVector(vector<int> &v) {
	for (vector<int> :: iterator it = v.begin(); it != v.end(); it++)
	{
		cout << *it << "\t";
	}
	cout << endl;
}

void test01() {
	vector<int> v1;
	v1.push_back(10);
	v1.push_back(20);
	v1.push_back(30);
	v1.push_back(40);
	v1.push_back(50);
	printVector(v1);

	v1.pop_back();
	printVector(v1);

	v1.insert(v1.begin(), 100);
	printVector(v1);

	v1.insert(v1.begin(), 2, 1000);
	printVector(v1);

	v1.erase(v1.begin());
	printVector(v1);

	//v1.erase(v1.begin(), v1.end());
	v1.clear();
	printVector(v1);
}

int main() {
	test01();

	return 0;
}

vector数据存取

功能描述

  对vector中的数据的存取操作

函数原型

  • at(int idx); //返回索引idx所指的数据
  • operator[]; //返回索引idx所指的数据
  • front(); //返回容器中第一个数据元素
  • back(); //返回容器中最后一个数据元素

示例

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

void test01() {
	vector<int>v1;
	for(int i = 0; i < 10; i++) {
		v1.push_back(i);
	}
	for (int i = 0; i < v1.size(); i++) {
		cout << v1[i] << "\t";
	}
	cout << endl;
	for (int i = 0; i < v1.size(); i++) {
		cout << v1.at(i) << "\t";
	}
	cout << endl;

	cout << v1.front() << endl;
	cout << v1.back() << endl;
}

int main() {
	test01();

	return 0;
}

vector互换容器

功能描述

  实现两个容器内元素进行互换

函数原型

  • swap(vec); //将vec与本身的元素互换

示例

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

void printVector(vector<int>v1) {
	for (vector<int>::iterator it = v1.begin(); it != v1.end(); it++) {
		cout << *it << "\t";
	}
	cout << endl;
}

void test01()
{
	vector<int> v1;
	for (int i = 1; i < 10; i++) {
		v1.push_back(i);
	}

	printVector(v1);

	vector<int>v2;
	for(int i = 10; i > 0; i--) {
		v2.push_back(i);
	}
	printVector(v2);

	v1.swap(v2);

	printVector(v1);
	printVector(v2);
}

//用途:可以收缩内存空间
void test02() {
	vector<int>v1;
	for (int i = 0; i < 100000; i++) {
		v1.push_back(i);
	}
	cout << v1.capacity() << endl;
	cout << v1.size() << endl;

	v1.resize(3);
	cout << v1.capacity() << endl;
	cout << v1.size() << endl;

	vector<int>(v1).swap(v1);
	cout << v1.capacity() << endl;
	cout << v1.size() << endl;
}

int main() {
	test01();
	test02();

	return 0;
}

vector预留空间

功能描述

  减少 vector 在动态拓展容量时的拓展次数

函数原型

  reserve(int len); //容器预留 len 个元素长度,预留位置不初始化,元素不可访问

示例

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

void test01() {
	vector<int> v1;
	v1.reserve(100000);
	int num = 0;
	int* p = NULL;
	for (int i = 0; i < 100000; i++) {
		v1.push_back(i);
		if (p != &v1[0]) {
			p = &v1[0];
			num++;
		}
	}
	cout << num << endl;
}

int main() {
	test01();

	return 0;
}

deque 容器

deque容器基本概念

功能

  双端数组,可以对头端进行插入删除操作

deque 与 vector 区别

  • vector 对于头部的插入删除效率低,数据量越大,效率越低
  • deque 相对而言,对头部的插入删除速度会比 vector 快
  • vector 访问元素时的速度会比 deque 快,这和两者内部实现有关

deque 内部工作原理:

  deque内部有一个中控器,维护每段缓冲区中的内容,缓冲区中存放真实数据

  中控器维护的是每个缓冲区的地址,使得使用 deque 时像一片连续的内存空间

deque 的迭代器也是支持随机访问的

deque 构造函数

功能描述

  deque容器构造

函数原型

  • deque<T> deq T; //默认构造形式
  • deque(beg,end); //构造函数将 [beg,end) 区间中的所有元素拷贝给自身
  • deque(n,elem); //构造函数将 n 个 elem 拷贝给本身
  • deque(const deque &deq); //拷贝构造函数

示例

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

void printDeque(deque<int> d) {
	for (deque<int>::iterator it = d.begin(); it != d.end(); it++) {
		cout << *it << " ";
	}
	cout << endl;
}

void test01() {
	deque<int>d1;
	for (int i = 1; i < 10; i++) {
		d1.push_back(i);
	}
	printDeque(d1);

	deque<int>d2(d1.begin(), d1.end());
	printDeque(d2);

	deque<int>d3(10, 100);
	printDeque(d3);

	deque<int>d4(d3);
	printDeque(d4);
}

int main() {
	test01();

	return 0;
}

deque 赋值操作

功能描述
  给 deque 容器进行赋值
函数原型
  deque& operator=(const deque &deq); //重载等号操作符
  assign(beg,end); //将 [beg,end) 区间中的数据拷贝赋值给本身
  assign(n,elem); //将 n 个 elem 拷贝赋值给本身
示例

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

void printDeque(const deque<int>& d) {
	for (deque<int>::const_iterator t = d.begin(); t != d.end(); t++) {
		cout << *t << " ";
	}
	cout << endl;
}

void test01() {
	deque<int> d1;
	for (int i = 0; i < 10; i++) {
		d1.push_back(i);
	}
	printDeque(d1);

	deque<int> d2 = d1;
	printDeque(d2);

	deque<int>d3;
	d3.assign(d2.begin(), d2.end());
	printDeque(d3);

	deque<int>d4;
	d4.assign(10, 2);
	printDeque(d4);
}

int main() {
	test01();

	return 0;
}

deque 大小操作

功能描述
  对 deque 容器的大小进行操作
函数原型
  deque.empty(); //判断容器是否为空
  deque.size(); //返回容器中元素的个数
  deque.resize(num); //重新指定容器的长度为 num,若容器变长,则以默认值填充新位置
           //如果容器变短,则末尾超出容器长度的元素被删除
  ·deque.resize(num,elem); //重新指定容器的长度为 num,若容器变长,则以elem填充新位置
           //如果容器变短,则末尾超出容器长度的元素被删除
示例

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

void printDeque(const deque<int>& d) {
	for (deque<int>::const_iterator t = d.begin(); t != d.end(); t++) {
		cout << *t << " ";
	}
	cout << endl;
}

void test01() {
	deque<int>d1;
	for (int i = 0; i < 10; i++) {
		d1.push_back(i);
	}
	printDeque(d1);

	if (d1.empty()) {
		cout << "empty" << endl;
	}
	else {
		cout << "not empty" << endl;
	}

	cout << d1.size() << endl;

	d1.resize(15);
	printDeque(d1);

	d1.resize(20, 1);
	printDeque(d1);

	d1.resize(15);
	printDeque(d1);
}

int main() {
	test01();

	return 0;
}

deque 插入和删除

功能描述
  向 deque 容器中插入和删除数据
函数原型
  两端插入操作:
    push_back(elem); //在容器尾部添加一个数据
    push_front(elem); //在容器头部插入一个数据
    pop_back(); //删除容器最后一个数据
    pop_front(); //删除容器最后一个数据
  指定位置操作:
    insert(pos,elem); //在 pos 位置插入一个 elem 元素的拷贝,返回新数据的位置
    insert(pos,n,elem); //在 pos 位置插入 n 个 elem 数据,无返回值
    insert(pos,beg,end); //在 pos 位置插入 [beg,end) 区间的数据,无返回值
    clear(); //清空容器中的所有数据
    erase(beg,end); //删除 [beg,end) 区间的数据,返回下一个数据的位置
    erase(pos); //删除 pos 位置的数据,返回下一个数据的位置
示例

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

void printDeque(const deque<int>& d) {
	for (deque<int>::const_iterator t = d.begin(); t != d.end(); t++) {
		cout << *t << " ";
	}
	cout << endl;
}

void test01() {
	deque<int> d1;
	d1.push_back(10);
	d1.push_back(20);
	
	d1.push_front(30);
	d1.push_front(40);
	printDeque(d1);

	d1.pop_back();
	printDeque(d1);

	d1.pop_front();
	printDeque(d1);
}

void test02() {
	deque<int> d1;
	d1.push_back(10);
	d1.push_back(20);
	d1.push_front(30);
	d1.push_front(40);
	printDeque(d1);

	d1.insert(d1.begin(), 50);
	printDeque(d1);

	d1.insert(d1.begin(), 2, 60);
	printDeque(d1);

	deque<int> d2;
	d2.push_back(1);
	d2.push_back(2);
	d2.push_back(3);
	d1.insert(d1.begin(), d2.begin(), d2.end());
	printDeque(d1);
}

void test03() {
	deque<int> d1;
	d1.push_back(10);
	d1.push_back(20);
	d1.push_front(30);
	d1.push_front(40);

	deque<int> ::iterator it = d1.begin();
	it++;
	d1.erase(it);
	printDeque(d1);

	d1.erase(d1.begin(), d1.end());
	printDeque(d1);

	d1.push_back(10);
	d1.push_back(20);
	d1.clear();
	printDeque(d1);
}

int main() {
	test01();
	test02();
	test03();

	return 0;
}

deque 数据存取

功能描述
  对 deque 中的数据的存取操作
函数原型
  at(int idx); //返回索引 idx 所指的数据
  operator[];; //返回索引 idx 所指的数据
  front(); //返回容器中第一个数据元素
  back(); //返回容器中最后一个数据元素
示例

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

void test01() {
	deque<int> d1;
	d1.push_back(10);
	d1.push_back(20);
	d1.push_back(30);
	d1.push_front(40);
	d1.push_front(50);
	d1.push_front(60);

	for (int i = 0; i < d1.size(); i++) {
		cout << d1[i] << " ";
	}
	cout << endl;

	for (int i = 0; i < d1.size(); i++) {
		cout << d1.at(i) << " ";;
	}
	cout << endl;

	cout << d1.front() << endl;
	cout << d1.back() << endl;
}

int main() {
	test01();

	return 0;
}

deque 排序

功能描述
  利用算法实现对 deque 容器进行排序
算法
  sort(iterator beg,iterator end) //对 beg 和 end 区间内元素进行排序
示例

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

void printDeque(const deque<int>& d) {
	for (deque<int>::const_iterator it = d.begin(); it != d.end(); it++) {
		cout << *it << " ";
	}
	cout << endl;
}

void test01() {
	deque<int> d;
	d.push_back(1);
	d.push_back(2);
	d.push_back(3);
	d.push_front(4);
	d.push_front(5);
	d.push_front(6);
	printDeque(d);
	
	sort(d.begin(), d.end());
	printDeque(d);
}

int main() {
	test01();

	return 0;
}

stack 容器

stack 基本概念

  概念:stack 是一种先进后出的数据结构,它只有一个出口。栈中只有顶端的元素才可以被外界使用,因此栈不允许有遍历行为

stack 常用接口

  功能描述:栈容器常用的对外接口

  构造函数
    stack<T> stk; //stack 采用模板类实现,stack 对象的默认构造形式
    stack(const stack &stk); //拷贝构造函数
  赋值操作:
    stack& operator=(const stack &stk); //重载等号操作符
  数据存取:
    push(elem); //向栈顶添加元素
    pop(); //从栈顶移除第一个元素
    top(); //返回栈顶元素
  大小操作:
    empty(); //判断堆栈是否为空
    size(); //返回栈的大小
示例

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

void test01() {
	stack<int> s;
	s.push(1);
	s.push(2);
	s.push(3);

	while (!s.empty()) {
		cout << s.top() << " ";
		s.pop();
	}
	cout << endl;

	cout << s.size() << endl;
}

int main() {
	test01();

	return 0;
}

queue 容器

queue 基本概念

概念:Queue 是一种先进先出的数据结构,它有两个出口

队列容器允许从一端新增元素,从另一端移除元素
队列中只有对头和队尾才可以被外界使用,因此队列不允许有遍历行为
队列中进数据称为——入队 push
队列中出数据称为——出队 pop

queue 常用接口

功能描述:栈容器常用的对外接口

构造函数:
queue<T> que; //queue 采用模板类实现,queue 对象的默认构造形式
queue(const queue &que); //拷贝构造函数
赋值操作:
queue& operator=(const queue &que); //重载等号操作符
数据存取:
push(elem); //往队尾添加元素
pop(); //从队头移除第一个元素
back(); //返回最后一个元素
front(); //返回第一个元素
大小操作:
empty(); //判断堆栈是否为空
size(); //返回栈的大小

示例

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

class Person {
public:
	Person(string name, int age) {
		this->age = age;
		this->name = name;
	}

	string name;
	int age;
};

void test01() {
	queue<Person> q;

	Person p1("a", 1);
	Person p2("b", 2);
	Person p3("c", 3);
	Person p4("d", 4);

	q.push(p1);
	q.push(p2);
	q.push(p3);
	q.push(p4);

	while (!q.empty()) {
		cout << q.front().name << " " << q.front().age << ";";
		cout << q.back().name << " " << q.back().age << endl;
		q.pop();
	}
	cout << q.size() << endl;
}

int main() {
	test01();
	
	return 0;
}

list 容器

list 基本概念

功能:将数据进行链式存储
链表是一种物理存储单元上非连续的存储结构,数据元素的逻辑顺序是通过链表中的指针链接实现的

链表的组成:链表由一系列节点组成

节点的组成:一个是存储数据元素的数据域,另一个是存储下一个结点地址的指针域

STL 中的链表是一个双向循环链表

由于链表的存储方式并不是连续的内存空间,因此链表 list 中的迭代器只支持前移和后移,属于双向迭代器

list 的优点:

  • 采用动态存储分配,不会造成内存浪费和溢出
  • 链表执行插入和删除操作十分方便,修改指针即可,不需要移动大量元素

list 的缺点:

  • 链表灵活,但是空间(指针域)和事件(遍历)额外耗费较大

list 有一个重要的性质,插入操作和删除操作都不会造成原有 list 迭代器的失效,这在 vector 是不成立的

list 构造函数

功能描述:创建 list 容器

函数原型

  • list<T> lst; //list 采用模板类实现,对象的默认构造形式
  • list(beg,end); //构造函数将 [beg,end) 区间中的元素拷贝给本身
  • list(n,elem); //构造函数将 n 个 elem 拷贝给本身
  • list(const list &lst); //拷贝构造函数

示例

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

void printList(const list<int>& l) {
	for (list<int>::const_iterator it = l.begin(); it != l.end(); it++) {
		cout << *it << " ";
	}
	cout << endl;
}

void test01() {
	list<int> l;
	l.push_back(10);
	l.push_back(20);
	l.push_back(30);
	l.push_back(40);
	printList(l);

	list <int> l1(l.begin(), l.end());
	printList(l1);

	list<int> l2 = l1;
	printList(l2);

	list<int> l3(10, 1000);
	printList(l3);
}

int main() {
	test01();

	return 0;
}

list 赋值与交换

功能描述:给 list 容器进行赋值,以及交换 list 容器
函数原型

  • assign(beg,end); //将 [beg,end) 区间中的数据拷贝赋值给本身
  • assign(n,elem); //将 n 个 elem 拷贝赋值给本身
  • list& operator=(coonst list &lst); //重载等号操作符
  • swap(lst); //将 lst 与本身的元素交换

示例

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

void printList(const list<int>& l) {
	for (list<int>::const_iterator it = l.begin(); it != l.end(); it++) {
		cout << *it << " ";
	}
	cout << endl;
}

void test01() {
	list<int> l;
	l.push_back(10);
	l.push_back(20);
	l.push_back(30);
	l.push_back(40);
	printList(l);

	list<int> l1 = l;
	printList(l1);

	list<int> l2;
	l2.assign(l1.begin(), l1.end());
	printList(l2);

	list<int> l3;
	l3.assign(10, 100);
	printList(l3);
}

void test02() {
	list<int> l;
	l.push_back(10);
	l.push_back(20);
	l.push_back(30);
	l.push_back(40);
	printList(l);

	list<int> l1;
	l1.assign(10, 100);
	printList(l1);

	l.swap(l1);
	printList(l);
	printList(l1);
}

int main() {
	test01();
	test02();

	return 0;
}

list 大小操作

功能描述:对 list 容器的大小进行操作
函数原型

  • size(); //返回容器中的元素个数
  • empty(); // 判断容器是否为空
  • resize(num); //重新指定容器的长度为num,若容器变长,则以默认值填充新位置;如果容器变短,则末尾超出容器长度的元素被删除
  • resize(num,elem); //重新指定容器的长度为num,若容器变长,则以elem值填充新位置;如果容器变短,则末尾超出容器长度的元素被删除

示例

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

void printList(const list<int>& l) {
	for (list<int>::const_iterator it = l.begin(); it != l.end(); it++) {
		cout << *it << " ";
	}
	cout << endl;
}

void test01() {
	list<int>l;
	l.push_back(10);
	l.push_back(20);
	l.push_back(30);
	l.push_back(40);
	printList(l);

	if (l.empty()) {
		cout << "empty" << endl;
	}
	else {
		cout << "not empty" << endl;
		cout << l.size() << endl;
	}

	l.resize(10,100);
	printList(l);

	l.resize(3);
	printList(l);
}

int main() {
	test01();

	return 0;
}

list 插入和删除

功能描述:对 list 容器进行数据的插入和删除
函数原型

  • push_back(elem); //在容器尾部加入一个元素
  • pop_back(); //删除容器中最后一个元素
  • push_front(elem); //在容器开头插入一个元素
  • pop_front(); //从容器开头移除第一个元素
  • insert(pos,elem); //在 pos 位置插 elem 元素的拷贝,返回新数据的位置
  • insert(pos,n,elem); //在 pos 位置插入 n 个 elem 数据,无返回值
  • insert(pos,beg,end)); //在 pos 位置插入 [beg,end) 区间的数据,无返回值
  • clear(); //移除容器的所有数据
  • erase(beg,end); //删除 [beg,end) 区间的数据,返回下一个数据的位置
  • erase(pos); //删除 pos 位置的数据,返回下一个数据的位置
  • remove(elem); //删除容器中所有与 elem 值匹配的元素

示例

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

void printList(const list<int>& l) {
	for (list<int>::const_iterator it = l.begin(); it != l.end(); it++) {
		cout << *it << " ";
	}
	cout << endl;
}

void test01() {
	list<int> l;
	l.push_back(10);
	l.push_back(20);
	l.push_back(30);
	l.push_front(100);
	l.push_front(200);
	l.push_front(300);
	printList(l);

	l.pop_back();
	printList(l);

	l.pop_front();
	printList(l);

	l.insert(l.begin(), 1000);
	printList(l);

	list<int>::iterator it = l.begin();
	l.erase(++it);
	printList(l);

	l.remove(1000);
	printList(l);

	l.clear();
	printList(l);
}

int main() {
	test01();

	return 0;
}

list 数据存取

功能描述:对 list 容器中数据进行存取
函数原型

  • front(); //返回第一个元素
  • back(); //返回最后一个元素

示例

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

void test01() {
	list<int> l;
	l.push_back(10);
	l.push_back(20);
	l.push_back(30);
	l.push_back(40);

	cout << l.front() << endl;
	cout << l.back() << endl;
}

int main() {
	test01();

	return 0;
}

list 反转和排序

功能描述:将容器中的元素反转,以及将容器中的数据进行排序
函数原型

  • reverse(); //反转链表
  • sort(); //链表排序

示例

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

void printList(const list<int>& l) {
	for (list<int>::const_iterator it = l.begin(); it != l.end(); it++) {
		cout << *it << " ";
	}
	cout << endl;
}

bool myCompare(int v1,int v2) {
	return v1 > v2;
}

void test01() {
	list<int> l;
	l.push_back(10);
	l.push_back(50);
	l.push_back(30);
	l.push_back(40);
	l.push_back(20);
	printList(l);

	l.reverse();
	printList(l);

	l.sort();
	printList(l);

	l.sort(myCompare);
	printList(l);
}

int main() {
	test01();

	return 0;
}

排序案例

案例描述:将 Person 自定义数据类型进行排序,Person 中属性有姓名、年龄、身高
排序规则:按照年龄进行升序,如果年龄相同按照身高进行降序
示例:

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

class Person {
public:
	Person(string name, int age, int height) {
		this->m_Age = age;
		this->m_Height = height;
		this->m_Name = name;
	}

	string m_Name;
	int m_Age;
	int m_Height;
};

bool myCompare(Person& p1, Person& p2) {
	if (p1.m_Age != p2.m_Age) {
		return p1.m_Age < p2.m_Age;
	}
	else {
		return p1.m_Height > p2.m_Height;
	}
}

void test01() {
	list<Person> l;
	Person p1("刘备", 35, 175);
	Person p2("关羽", 35, 200);
	Person p3("张飞", 35, 160);
	Person p4("赵云", 25, 190);
	Person p5("曹操", 45, 180);
	Person p6("孙权", 40, 170);

	l.push_back(p1);
	l.push_back(p2);
	l.push_back(p3);
	l.push_back(p4);
	l.push_back(p5);
	l.push_back(p6);

	for (list<Person>::iterator it = l.begin(); it != l.end(); it++) {
		cout << (*it).m_Name << " " << (*it).m_Age << " " << (*it).m_Height << endl;
	}
	cout << "sort" << endl;

	l.sort(myCompare);

	for (list<Person>::iterator it = l.begin(); it != l.end(); it++) {
		cout << (*it).m_Name << " " << (*it).m_Age << " " << (*it).m_Height << endl;
	}
}

int main() {
	test01();

	return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值