C++学习笔记(十五)vector及string

STL初始

1.STL的诞生

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

2.STL基本概念

//STL(Standard Template Library,标准模板库)
//STL 从广义上分为: 容器(container) 算法(algorithm) 迭代器(iterator)
//容器和算法之间通过迭代器进行无缝连接。
//STL 几乎所有的代码都采用了模板类或者模板函数

3.STL六大组件

//STL大体分为六大组件,分别是:容器、算法、迭代器、仿函数、适配器(配接器)、空间配置器
//1. 容器:各种数据结构,如vector、list、deque、set、map等, 用来存放数据。
//2. 算法:各种常用的算法,如sort、?nd、copy、for_each等
//3. 迭代器:扮演了容器与算法之间的胶合剂。
//4. 仿函数:行为类似函数,可作为算法的某种策略。
//5. 适配器:一种用来修饰容器或者仿函数或迭代器接口的东西。
//6. 空间配置器:负责空间的配置与管理。

4.STL中容器、算法、迭代器

//容器:置物之所也
//STL容器就是将运用广泛的一些数据结构实现出来 常用的数据结构:数组, 链表, 树, 栈, 队列, 集合, 映射表 等
//这些容器分为序列式容器和关联式容器两种 :
//序列式容器:强调值的排序,序列式容器中的每个元素均有固定的位置。
//关联式容器 : 二叉树结构,各元素之间没有 严格的物理上的顺序关系

//算法:问题之解法也
//有限的步骤,解决逻辑或数学上的问题,这一门学科我们叫做算法(Algorithms)
//算法分为 : 质变算法和非质变算法。
//质变算法:是指运算过程中会更改区间内的元素的内容。例如拷贝,替换,删除等等
//非质变算法:是指运算过程中不会更改区间内的元素内容,例如查找、计数、遍历、寻找极值等等

//迭代器:容器和算法之间粘合剂
//提供一种方法,使之能够依序寻访某个容器所含的各个元素,而又无需暴露该容器的内部表示方式。
//每个容器都有自己专属的迭代器
//迭代器使用非常类似于指针,初学阶段我们可以先理解迭代器为指针
//迭代器种类:
//输入迭代器 对数据的只读访问 只读,支持++、==、!=
//输出迭代器 对数据的只写访问 只写,支持++
//前向迭代器 读写操作,并能向前推进迭代器 读写,支持++、 == 、! =
//双向迭代器 读写操作,并能向前和向后操作 读写,支持++、–,
//随机访问迭代器 读写操作,可以以跳跃的方式访问任意数据,功能最强的迭代器 读写,支持++、–、[n]、 - n、<、 <= 、>、 >=
//常用的容器中迭代器种类为双向迭代器,和随机访问迭代器
在这里插入图片描述

5.容器算法迭代器初始

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

5.1vector存放内置数据类型//需要包含头文件#incude

//容器:vector
//算法:for_each
//迭代器: vector::iterator

//通过迭代器访问容器中的数据
vector<int>::iterator itbegin = v.begin();  //.begin()是起始迭代器,指向容器中第一个元素,本质是指针
vector<int>::iterator itend = v.end();  //.end()是结束迭代器,指向容器中最后一个元素的下一个位置,本质是指针
//利用STL提供的遍历算法,需要包含头文件#include<algorithm>
for_each(v.begin(), v.end(), myPrint_vector);//只写函数名就可以,不用加括号

代码:

/*vector存放内置数据类型*/
//需要包含头文件#incude<vector>
//容器:vector
//算法:for_each
//迭代器: vector<int>::iterator

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

void test23()
{
	//创建一个vector容器,认为是一个数组
	vector<int> v;

	//向容器中插入数据,通过.push_back(数据)进行尾插
	v.push_back(10);
	v.push_back(20);
	v.push_back(30);
	v.push_back(40);

	//通过迭代器访问容器中的数据
	vector<int>::iterator itbegin = v.begin();  //.begin()是起始迭代器,指向容器中第一个元素,本质是指针
	vector<int>::iterator itend = v.end();  //.end()是结束迭代器,指向容器中最后一个元素的下一个位置,本质是指针

	//第一种遍历方式
	while (itbegin != itend)
	{
		cout << *itbegin << endl;//解引用取出数据
		itbegin++;
	}

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

	//第三种遍历方式
	//利用STL提供的遍历算法,需要包含头文件#include<algorithm>
	for_each(v.begin(), v.end(), myPrint_vector);//只写函数名就可以,不用加括号
}

main函数:

	/*vector存放内置数据类型*/
	test23();

5.2vector存放自定义数据类型

//遍历容器中的数据 通过(*it).对姓名和年龄进行取值,或者通过it->进行取值
//for里的<>里是啥数据类型,it就是指向什么数据类型的指针,这里it是一个Person_v的指针
//通过(*it).可以取出Person_v里的成员属性或者通过it->得到成员属性
for (vector<Person_v>::iterator it = v.begin(); it != v.end(); it++)
{
	cout << "姓名:" << (*it).m_Name << "\t年龄:" << (*it).m_Age << endl;
	cout << "姓名:" << it->m_Name << "\t年龄:" << it->m_Age << endl;

}


//遍历容器
//for里面的<>是什么数据类型,it就是指向什么数据类型的指针,这里it是一个Person_v*的指针(指针的指针)
//通过(*it)可以得到Person_v的指针,再通过(*(*it)).或者(*it)->可以得到成员属性
for (vector<Person_v*>::iterator it = v.begin(); it != v.end(); it++)
{
	cout << "---------------------------" << endl;
	cout << "姓名:" << (*(*it)).m_Name << "\t年龄:" << (*(*it)).m_Age << endl;
	cout << "姓名:" << (*it)->m_Name << "\t年龄:" << (*it)->m_Age << endl;
}

代码:

/*vector存放自定义数据类型*/

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

public:
	int m_Age;
	string m_Name;
};

void test24()
{
	vector<Person_v> v;

	Person_v p1("a", 1);
	Person_v p2("b", 1);
	Person_v p3("c", 1);
	Person_v p4("d", 1);
	Person_v p5("e", 1);

	//向容器中添加数据
	v.push_back(p1);
	v.push_back(p2);
	v.push_back(p3);
	v.push_back(p4);
	v.push_back(p5);

	//遍历容器中的数据 通过(*it).对姓名和年龄进行取值,或者通过it->进行取值
	//for里的<>里是啥数据类型,it就是指向什么数据类型的指针,这里it是一个Person_v的指针
	//通过(*it).可以取出Person_v里的成员属性或者通过it->得到成员属性
	for (vector<Person_v>::iterator it = v.begin(); it != v.end(); it++)
	{
		cout << "姓名:" << (*it).m_Name << "\t年龄:" << (*it).m_Age << endl;
		cout << "姓名:" << it->m_Name << "\t年龄:" << it->m_Age << endl;

	}
}

//存放自定义数据类型的指针
void test25()
{
	vector<Person_v*> v;

	Person_v p1("a", 1);
	Person_v p2("b", 1);
	Person_v p3("c", 1);
	Person_v p4("d", 1);
	Person_v p5("e", 1);

	//向容器中添加数据
	v.push_back(&p1);
	v.push_back(&p2);
	v.push_back(&p3);
	v.push_back(&p4);
	v.push_back(&p5);

	//遍历容器
	//for里面的<>是什么数据类型,it就是指向什么数据类型的指针,这里it是一个Person_v*的指针(指针的指针)
	//通过(*it)可以得到Person_v的指针,再通过(*(*it)).或者(*it)->可以得到成员属性
	for (vector<Person_v*>::iterator it = v.begin(); it != v.end(); it++)
	{
		cout << "---------------------------" << endl;
		cout << "姓名:" << (*(*it)).m_Name << "\t年龄:" << (*(*it)).m_Age << endl;
		cout << "姓名:" << (*it)->m_Name << "\t年龄:" << (*it)->m_Age << endl;
	}
}

main函数:

	/*vector存放自定义数据类型*/
	test24();
	test25();

5.3vector容器嵌套容器

代码:

/*vector容器嵌套容器*/
void test26()
{
	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 + 1);
		v2.push_back(i + 2);
		v3.push_back(i + 3);
		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++)
	{
		//it是指向容器的指针,(*it)是容器
		for (vector<int>::iterator vit = (*it).begin(); vit != (*it).end(); vit++)
		{
			cout << *vit << endl;
		}
		cout << endl;
	}
}

main函数:

	/*vector容器嵌套容器*/
	test26();

STL-常用容器

string容器

1.string基本概念

//本质:
//string是C++风格的字符串,而string本质上是一个类
//string和char * 区别:
//char * 是一个指针 string是一个类,类内部封装了char*,管理这个字符串,是一个char型的容器。
//特点:
//string 类内部封装了很多成员方法
//例如:查找find,拷贝copy,删除delete 替换replace,插入insert
//string管理char
所分配的内存,不用担心复制越界和取值越界等,由类内部进行负责

2.string构造函数

//构造函数原型:
//string(); //创建一个空的字符串 例如: string str;
//string(const char* s); //使用字符串s初始化
//string(const string& str); //使用一个string对象初始化另一个string对象
//string(int n, char c); //使用n个字符c初始化
代码:

/*string构造函数*/
//构造函数原型:
	//string();   //创建一个空的字符串 例如: string str; 
	//string(const char* s); //使用字符串s初始化 
	//string(const string& str); //使用一个string对象初始化另一个string对象 
	//string(int n, char c);   //使用n个字符c初始化 
void test27()
{
	string s1;//默认构造
	const char *str = "Hello world!";
	string s2(str);//使用字符串初始化
	cout << "s2=" << s2 << endl;
	string s3(s2);//拷贝构造,使用一个string对象初始化另一个string
	cout << "s3=" << s3 << endl;
	string s4(10, 'a');//10个a
	cout << "s4=" << s4 << endl;
}

main函数:

	/*string构造函数*/
	test27();

3.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赋给当前字符串

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

代码:

/*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赋给当前字符串

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

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

	string str3;
	str3 = '3';
	cout << "str3=" << str3 << endl;

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

	string str5;
	str5.assign("helloworld", 5);
	cout << "str5=" << str5 << endl;

	string str6;
	str6.assign("Hello C++");
	cout << "str6=" << str6 << endl;

	string str7;
	str7.assign(10, 'w');//10个w
	cout << "str7=" << str7 << endl;
}

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

main函数:

	/*string赋值操作*/
	test28();

4.string字符串拼接

//功能描述:
//实现在字符串末尾拼接字符串
//函数原型:
//string& operator+=(const char* str);//重载+=操作符
//string& operator+=(const char c);//重载+=操作符
//string& operator+=(const string& str);//重载+=操作符
//string& append(const char *s);//把字符串s连接到当前字符串结尾
//string& append(const char *s, int n);//把字符串s的前n个字符连接到当前字符串结尾
//string& append(const string &s);//同operator+=(const string& str)
//string& append(const string &s, int pos, int n); //字符串s中从第pos个位置开始的n个字符连接到字符串结尾
代码:

/*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个字符连接到字符串结尾

void test29()
{
	string str1 = "我";
	str1 += "爱学习";
	cout << "str1=" << str1 << endl;

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

	string str2 = "数学、C++";
	str1 += str2;
	cout << "str1=" << str1 << endl;

	string str3 = "I";
	str3.append(" love");
	cout << "str3=" << str3 << endl;

	str3.append(" game abcf", 5);
	cout << "str3=" << str3 << endl;

	str3.append(str2);
	cout << "str3=" << str3 << endl;

	string str4 = "helloworld";
	str3.append(str4, 5, 5);
	cout << "str3=" << str3 << endl;

}

main函数:

	/*string字符串拼接*/
	test29();

5.string查找和替换

//功能描述:
//查找:查找指定字符串是否存在
//替换:在指定的位置替换字符串
//函数原型:
//int find(const string& str, int pos = 0) const; //查找str第一次出现位置,从pos开始查找
//int find(const char* s, int pos = 0) const; //查找s第一次出现位置,从pos开始查找
//int find(const char* s, int pos, int n) const; //从pos位置查找s的前n个字符第一次位置
//int find(const char c, int pos = 0) const; //查找字符c第一次出现位置
//int rfind(const string& str, int pos = npos) const; //查找str后一次位置,从pos开始查找
//int rfind(const char* s, int pos = npos) const; //查找s后一次出现位置,从pos开始查找
//int rfind(const char* s, int pos, int n) const; //从pos查找s的前n个字符后一次位置
//int rfind(const char c, int pos = 0) const; //查找字符c后一次出现位置
//string& replace(int pos, int n, const string& str); //替换从pos开始n个字符为字符串str
//string& replace(int pos, int n,const char* s);//替换从pos开始的n个字符为字符串s

//rfind和find的区别
	//rfind从右往左查找;find从左往右查找

//总结:
//find查找是从左往后,rfind从右往左
//find找到字符串后返回查找的第一个字符位置,找不到返回 - 1
//replace在替换时,要指定从哪个位置起,多少个字符,替换成什么样的字符串
代码:

/*string查找和替换*/
//功能描述:
	//查找:查找指定字符串是否存在 
	//替换:在指定的位置替换字符串
//函数原型:
//int find(const string& str, int pos = 0) const; //查找str第一次出现位置,从pos开始查找 
//int find(const char* s, int pos = 0) const; //查找s第一次出现位置,从pos开始查找 
//int find(const char* s, int pos, int n) const; //从pos位置查找s的前n个字符第一次位置 
//int find(const char c, int pos = 0) const; //查找字符c第一次出现位置 
//int rfind(const string& str, int pos = npos) const; //查找str后一次位置,从pos开始查找 
//int rfind(const char* s, int pos = npos) const; //查找s后一次出现位置,从pos开始查找 
//int rfind(const char* s, int pos, int n) const; //从pos查找s的前n个字符后一次位置 
//int rfind(const char c, int pos = 0) const; //查找字符c后一次出现位置 
//string& replace(int pos, int n, const string& str); //替换从pos开始n个字符为字符串str 
//string& replace(int pos, int n,const char* s);//替换从pos开始的n个字符为字符串s

void test30()
{
	string str1 = "abcdefgde";
	int pos = str1.find("df");

	cout << "pos=" << pos << endl;

	if (pos == -1)
	{
		cout << "未找到字符串" << endl;
	}
	else
	{
		cout << "找到字符串" << endl;
	}
	//rfind和find的区别
		//rfind从右往左查找;find从左往右查找
	pos = str1.rfind("de");
	cout << "pos=" << pos << endl;
}

void test31()
{
	string str1 = "abcdefg";

	//从1号位置起,3个字符替换为“1111”
	str1.replace(1, 3, "1111");
	cout << "str1=" << str1 << endl;
}
//总结:
	//find查找是从左往后,rfind从右往左 
	//find找到字符串后返回查找的第一个字符位置,找不到返回 - 1 
	//replace在替换时,要指定从哪个位置起,多少个字符,替换成什么样的字符串

main函数:

	/*string查找和替换*/
	test30();
	test31();

6.string字符串比较

//功能描述:
//字符串之间的比较
//比较方式:
//字符串比较是按字符的ASCII码进行对比
//= 返回 0
//> 返回 1
//< 返回 - 1
//函数原型:
//int compare(const string &s) const;//与字符串s比较
//int compare(const char *s) const;//与字符串s比较

//总结:字符串对比主要是用于比较两个字符串是否相等,判断谁大谁小的意义并不是很大
代码:

/*string字符串比较*/
//功能描述:
	//字符串之间的比较
//比较方式:
	//字符串比较是按字符的ASCII码进行对比
	//= 返回 0 
	//> 返回 1 
	//< 返回 - 1
//函数原型:
	//int compare(const string &s) const;//与字符串s比较 
	//int compare(const char *s) const;//与字符串s比较

void test32()
{
	string str1 = "hello";
	string str2 = "xello";

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

}

//总结:字符串对比主要是用于比较两个字符串是否相等,判断谁大谁小的意义并不是很大

main函数:

	/*string字符串比较*/
	test32();

7.string字符存取

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

for (int i = 0; i < str.size(); i++)//string.size()用来输出字符串长度

//总结:string字符串中单个字符存取有两种方式,利用 [ ] 或 at

代码:

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

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

	cout << "str=" << str << endl;

	for (int i = 0; i < str.size(); i++)//string.size()用来输出字符串长度
	{
		cout << str[i] << " " ;
		
	}
	cout << endl;

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

	//修改单个字符
	str[0] = 'x';
	cout << "str=" << str << endl;

	str.at(1) = 'x';
	cout << "str=" << str << endl;
}
//总结:string字符串中单个字符存取有两种方式,利用 [ ] 或 at

main函数:

	/*string字符存取*/
	test33();

8.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个字符

//总结:插入和删除的起始下标都是从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个字符 

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

	//插入
	str.insert(1, "111");
	cout << "str=" << str << endl;//h111ello

	//删除
	str.erase(1, 3);
	cout << "str=" << str << endl;//hello
}
//总结:插入和删除的起始下标都是从0开始

main函数:

	/*string插入和删除*/
	test34();

9.string子串

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

//总结:灵活的运用求子串功能,可以在实际开发中获取有效的信息

代码:

/*string子串*/
//功能描述:
	//从字符串中获取想要的子串
//函数原型:
	//string substr(int pos = 0, int n = npos) const;//返回由pos开始的n个字符组成的字符串
void test35()
{
	string str = "abcdef";
	cout << "str=" << str << endl;
	string subStr = str.substr(1, 3);
	cout << "str=" << subStr << endl;
}

//实用操作
void test36()
{
	string email = "hello@sina.com";

	//从邮件地址中获取用户名信息
	int pos = email.find("@");

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

//总结:灵活的运用求子串功能,可以在实际开发中获取有效的信息

main函数:

	/*string子串*/
	test35();
	test36();

代码

#include<iostream>
#include<string>
//第一种解决方式,直接包含源文件
//因为类模板中成员函数创建时机是在调用阶段,导致分文件编写时链接不到
//#include"Person_fwj.cpp"
//第二种解决方式,将.h和.cpp中的内容写到一起,将后缀名改为.hpp文件
#include "Person_fwj.hpp"

#include"MyArry.hpp"

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



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



/*函数模板语法*/

//函数模板作用:
//建立一个通用函数,其函数返回值类型和形参类型可以不具体制定,用一个虚拟的类型来代表。
//语法:
	//template<typename T>
	//函数声明或定义

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

//交换两个整型的函数
void swapInt(int &a, int &b)
{
	int temp = a;
	a = b;
	b = temp;
}

//交换两个浮点型函数
void swapDouble(double &a, double &b)
{
	double temp = a;
	a = b;
	b = temp;
}

//函数模板
template<typename T> //声明一个模板,告诉编译器后面代码中紧跟着的T不要报错,T是一个通用数据类型
void mySwap(T &a, T &b)
{
	T temp = a;
	a = b;
	b = temp;
}

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

	//swapInt(a, b);
	//利用函数模板交换
	//两种方式使用函数模板
	//1.自动类型推导
	mySwap(a, b);//自己推出是整型
	//2显示指定类型
	mySwap<int>(a, b);//通过<int>指定是整型

	cout << "a=" << a << "\tb=" << b << endl;

	double c = 1.1;
	double d = 1.2;

	swapDouble(c, d);

	cout << "c=" << c << "\td=" << d << endl;

	/*函数模板注意事项*/
	//1.自动类型推导,必须推导出一致的数据类型T,才可以使用
	//2.模板必须要确定出T的数据类型,才可以使用
	//mySwap(a, c); //错误,推导不出一致的T类型,因为c是double

}

//模板必须要确定出T的数据类型,才可以使用
template<typename T>
void func01()
{
	cout << "func的调用" << endl;
}

void test02()
{
	//func01;//错误,模板不能独立使用,必须确定出T的类型 
	func01<int>();//正确,利用显示指定类型的方式,给T一个类型,才可以使用该模板
}

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





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

//交换函数模板
template<typename T>
void mySwap2(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++)
		{
			//认定的最大值比遍历出的数值要小,说明j下标的元素才是真正的最大值
			if (arr[max] < arr[j])
			{
				max = j;
			}
		}
		if (max != i)
		{
			//交换max和i元素
			mySwap2(arr[max], arr[i]);
		}
	}
}

//提供打印数组模板
template<typename T>
void printArry(T arr[],int len)
{
	for (int i = 0; i < len; i++)
	{
		cout << arr[i] << " ";
		cout << endl;
	}
}

void test03()
{
	//测试char数组
	char charARR[] = "bacdfe";
	mySort(charARR, sizeof(charARR) / sizeof(char));
	printArry(charARR, sizeof(charARR) / sizeof(char));

	//测试int数组
	int intArr[] = { 1,5,4,3,6,2,7,0 };
	mySort(intArr, sizeof(intArr) / sizeof(int));
	printArry(intArr, sizeof(intArr) / sizeof(int));

}




/*普通函数与函数模板的区别*/
//区别
	//1.普通函数调用时可以发生自动类型转换(隐式类型转换)
	//2.函数模板调用时,如果利用自动类型推导,不会发生隐式类型转换
	//3.如果利用显示指定类型的方式,可以发生隐式类型转换

//普通函数
int myAdd01(int a, int b)
{
	return a + b;
}

//函数模板
template<typename T>
T myAdd02(T a, T b)
{
	return a + b;
}

void test04()
{
	int a = 10;
	int b = 20;
	char c = 'c';
	cout << myAdd01(a, b) << endl;
	cout << myAdd01(a, c) << endl;//正确,将char类型的'c'隐式转换为int类型  'c' 对应 ASCII码 99

	cout << myAdd02(a, b) << endl;
	//cout << myAdd02(a, c) << endl;//报错,使用自动类型推导时,不会发生隐式类型转换
	cout << myAdd02<int>(a, c) << endl;//正确,如果用显示指定类型,可以发生隐式类型转换 
 
}
//总结:建议使用显示指定类型的方式,调用函数模板,因为可以自己确定通用类型




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

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

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

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

void test05()
{
	int a = 10;
	int b = 10;
	myPrint(a, b);

	//通过空模板参数列表,强制调用函数模板
	myPrint<>(a, b);
	//模板重载
	myPrint(a, b, 100);
	//如果函数模板产生更好地匹配,优先调用函数模板
	char c1 = 'a';
	char c2 = 'b';
	myPrint(c1, c2);//调用函数模板,因为如果调用普通函数还得进行类型转换,所以编译器为了省事直接调用模板函数
}
//总结:既然提供了函数模板,好就不要提供普通函数,否则容易出现二义性



/*模板的局限性*/
//模板并不是万能的,有些特定数据类型,需要用具体方式特殊实现

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



//对比两个数据是否相等函数
template<typename T>
bool myCompare(T &a, T &b)
{
	if (a == b)
	{
		return true;
	}
	else
	{
		return false;
	}
}

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

void test06()
{
	int a = 10;
	int b = 20;
	bool result = myCompare(a, b);
	if (result)
	{
		cout << "a=b" << endl;
	}
	else
	{
		cout << "a!=b" << endl;
	}
}

void test07()
{
	Person_jx p1("Tom", 10);
	Person_jx p2("Tom", 10);
	bool result = myCompare(p1, p2);
	if (result)
	{
		cout << "p1=p2" << endl;
	}
	else
	{
		cout << "p1!=p2" << endl;
	}

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




/*类模板语法*/
//语法:template<class 形参1, class 形参2...>
//类
template<class NameType,class AgeType>
class Person_lmb
{
public:
	Person_lmb(NameType name, AgeType age)//这里不能传引用,要不然实例化会报错
	{
		this->m_Age = age;
		this->m_Name = name;
	}

	void showPerson_lmb()
	{
		cout << "姓名:" << this->m_Age << "\t年龄:" << this->m_Age << endl;
	}

public:
	NameType m_Name;
	AgeType m_Age;
};

void test08()
{
	Person_lmb<string, int> p1("Tom", 18);
	p1.showPerson_lmb();
}






/*类模板与函数模板区别*/
//类模板与函数模板区别主要有两点:
	//1. 类模板没有自动类型推导的使用方式
	//2. 类模板在模板参数列表中可以有默认参数

template<class NameType, class AgeType=int>//可以有默认参数
class Person_qv
{
public:
	Person_qv(NameType name, AgeType age)
	{
		this->m_Name = name;
		this->m_Age = age;
	}

	void showPerson_qv()
	{
		cout << "姓名:" << this->m_Name << "\t年龄:" << this->m_Age << endl;
	}

public:
	NameType m_Name;
	AgeType m_Age;
};

//1.类模板没有自动类型推导使用方式
void test09()
{
	//Person_qv p("Tom", 200);//错误,无法自动类型推导
	Person_qv<string, int> p("Tom", 200);//正确,需要指定显示类型
	p.showPerson_qv();
}

//2.类模板在模板参数列表中可以有默认参数
void test10()
{
	Person_qv<string> p("Simi", 1000);
	p.showPerson_qv();
}



/*类模板中成员函数创建时机*/
//类模板中成员函数和普通类中成员函数创建时机是有区别的:
	//普通类中的成员函数一开始就可以创建 
	//类模板中的成员函数在调用时才创建
class Person_sj1
{
public:
	void showPerson_sj1()
	{
		cout << "Person_sj1 show" << endl;
	}
public:

};

class Person_sj2
{
public:
	void showPerson_sj2()
	{
		cout << "Person_sj2 show" << endl;
	}
public:

};

template <class T>

class myClass_sj
{
public:
	//类模板中的成员函数
	void func1()
	{
		obj.showPerson_sj1();
	}

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

public:
	T obj;
};

void test11()
{
	myClass_sj<Person_sj2> m;
	//m.func1();
	m.func2();
}
//总结:类模板中的成员函数并不是一开始就创建的,在调用时才去创建




/*类模板对象做函数参数*/
//学习目标:
	//类模板实例化出的对象,向函数传参的方式
//一共有三种传入方式:
	//1. 指定传入的类型-- - 直接显示对象的数据类型 
	//2. 参数模板化-- - 将对象中的参数变为模板进行传递 
	//3. 整个类模板化-- - 将这个对象类型 模板化进行传递

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

	void showPerson()
	{
		cout << "姓名:" << this->m_Name << "\t年龄:" << this->m_Age << endl;
	}
public:
	T1 m_Name;
	T2 m_Age;
};


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

void test12()
{
	Person_hx<string, int> p("Tom", 100);
	printPerson1(p);
}

//参数模板化
//看数据类型用typeid(变量名).name()
template<class T1, class T2>
void printPerson2(Person_hx<T1,T2> &p)
{
	p.showPerson();
	cout << "T1的类型为:" << typeid(T1).name() << endl;
	cout << "T2的类型为:" << typeid(T2).name() << endl;

}

void test13()
{
	Person_hx<string, int> p("SImi", 200);
	printPerson2(p);
}

//3.整个类模板化
template<class T>
void printPerson3(T & p)
{
	p.showPerson();
	cout << "T的数据类型:" << typeid(T).name() << endl;
}

void test14()
{
	Person_hx<string, int>p("Jier", 400);
	printPerson3(p);
}
//总结:
//通过类模板创建的对象,可以有三种方式向函数中进行传参 
//使用比较广泛是第一种:指定传入的类





/*类模板与继承*/
//当类模板碰到继承时,需要注意一下几点:
	//当子类继承的父类是一个类模板时,子类在声明的时候,要指定出父类中T的类型 
	//如果不指定,编译器无法给子类分配内存 
	//如果想灵活指定出父类中T的类型,子类也需变为类模板

template<class T>
class Base1
{
public:

public:
	T m;
};

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

public:

};

void test15()
{
	Son1 s;
}

//如果想灵活指定父类中T类型,子类也需要变类模板
template<class T1,class T2>
class Son2 :public Base1<T2>
{
public:
	Son2()
	{
		cout << "T1的数据类型为:" << typeid(T1).name() << "\tT2的数据类型为:" << typeid(T2).name() << endl;
	}
public:
	T1 obj;
};

void test16()
{
	Son2<int, char>s2;//int传给T1,char传给T2,T2也就是父类的数据类型
}
//总结:如果父类是类模板,子类需要指定出父类中T的数据类型




/*类模板成员函数类外实现*/
template <class T1,class T2>
class Person_sx
{
public:
	Person_sx(T1 name, T2 age);
	//{
	//	this->m_Age = age;
	//	this->m_Name = name;
	//}

	void showPerson();
	//{
	//	cout << "姓名:" << this->m_Name << "\t年龄:" << this->m_Age << endl;
	//}
public:
	T1 m_Name;
	T2 m_Age;
};

//构造函数类外实现
template<class T1,class T2>
Person_sx<T1,T2>::Person_sx(T1 name, T2 age)//在::前加<T1,T2>
{
	this->m_Age = age;
	this->m_Name = name;
}

//成员函数类外实现
template<class T1,class T2>
void Person_sx<T1, T2>::showPerson()//在::前加<T1,T2>
{
	cout << "姓名:" << this->m_Name << "\t年龄:" << this->m_Age << endl;
}

void test17()
{
	Person_sx<string, int> p("Tom", 100);
	p.showPerson();
}
//总结:类模板中成员函数类外实现时,需要加上模板参数列表




/*类模板分文件编写*/
//问题:
	//类模板中成员函数创建时机是在调用阶段,导致分文件编写时链接不到
//解决:
	//解决方式1:直接包含.cpp源文件 
	//解决方式2:将声明和实现写到同一个文件中,并更改后缀名为.hpp,hpp是约定的名称,并不是强制
//template <class T1, class T2>
//class Person_fwj
//{
//public:
//	Person_fwj(T1 name, T2 age);
//
//	void showPerson();
//
//public:
//	T1 m_Name;
//	T2 m_Age;
//};

//template <class T1, class T2>
//Person_fwj<T1, T2>::Person_fwj(T1 name, T2 age)
//{
//	this->m_Name = name;
//	this->m_Age = age;
//}
//
//template <class T1, class T2>
//void Person_fwj<T1, T2>::showPerson()
//{
//	cout << "姓名:" << this->m_Name << "\t年龄:" << this->m_Age << endl;
//}

void test18()
{
	Person_fwj<string, int>p("Jerry", 18);
	p.showPerson();
}





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

//类外实现
//如果全局函数是类外实现,需要让编译器提前知道这个函数的存在

//提前让编译器知道Person类的存在
template<class T1, class T2>
class Person_yy;

template<class T1, class T2>
void printPerson_yy2(Person_yy<T1, T2>p)
{
	cout << "类外实现   姓名:" << p.m_Name << "\t年龄:" << p.m_Age << endl;
}

template <class T1, class T2>
class Person_yy
{
	//全局函数类内实现
	friend void printPerson_yy(Person_yy<T1, T2>p)
	{
		cout << "姓名:" << p.m_Name << "\t年龄:" << p.m_Age << endl;
	}

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


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

private:
	T1 m_Name;
	T2 m_Age;
};

//全局函数在类内实现调用
void test19()
{
	Person_yy<string, int> p("Hello", 120);
	printPerson_yy(p);
}

//全局函数在类外实现调用
void test20()
{
	Person_yy<string, int>p("Giao", 20);
	printPerson_yy2(p);

}
//总结:建议全局函数做类内实现,用法简单,而且编译器可以直接识别






/*类模板案例*/
//案例描述: 实现一个通用的数组类,要求如下:
	//可以对内置数据类型以及自定义数据类型的数据进行存储 
	//将数组中的数据存储到堆区 
	//构造函数中可以传入数组的容量 
	//提供对应的拷贝构造函数以及operator = 防止浅拷贝问题 
	//提供尾插法和尾删法对数组中的数据进行增加和删除 
	//可以通过下标的方式访问数组中的元素 
	//可以获取数组中当前元素个数和数组的容量
void printArray(MyArray<int> &arr)
{
	for (int i = 0; i < arr.getSize(); i++)
	{
		cout << arr[i] << endl;
	}
}


void test21()
{
	MyArray<int> arr1(5);

	for (int i = 0; i < 5; i++)
	{
		//利用尾插法向数组中插入数据
		arr1.PushBack(i);
	}
	cout << "arr1的打印输出:" << endl;
	printArray(arr1);
	cout << "arr1的容量:" << arr1.getCapacity() << endl;
	cout << "arr1的大小:" << arr1.getSize() << endl;


	MyArray<int>arr2(arr1);
	cout << "arr2的打印输出:" << endl;
	printArray(arr2);

	//尾删
	arr2.PopBack();
	cout << "arr2的容量:" << arr2.getCapacity() << endl;
	cout << "arr2的大小:" << arr2.getSize() << endl;

	MyArray<int>arr3(100);
	arr3 = arr1;
}

//测试自定义数据类型
class Person_zdy
{
public:
	Person_zdy()
	{

	}

	Person_zdy(string name, int age)
	{
		this->m_Age = age;
		this->m_Name = name;
	}

public:
	string m_Name;
	int m_Age;
};

void printPersonArray(MyArray<Person_zdy> &arr)
{
	for (int i = 0; i < arr.getSize(); i++)
	{
		cout << "姓名:" << arr[i].m_Name << "\t年龄:" << arr[i].m_Age << endl;
	}
}

void test22()
{
	MyArray<Person_zdy> arr(10);
	Person_zdy p1("Tom", 11);
	Person_zdy p2("Simi", 12);
	Person_zdy p3("REm", 13);
	Person_zdy p4("DFDm", 14);
	Person_zdy p5("SAGm", 15);

	//将数据插入到数组中
	arr.PushBack(p1);
	arr.PushBack(p2);
	arr.PushBack(p3);
	arr.PushBack(p4);
	arr.PushBack(p5);

	//打印数组
	printPersonArray(arr);

	//输出容量
	cout << "arr的容量为:" << arr.getCapacity() << endl;

	//输出大小
	cout << "arr的大小为:" << arr.getSize() << endl;
}






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



/*STL基本概念*/
//STL(Standard Template Library,标准模板库) 
//STL 从广义上分为: 容器(container) 算法(algorithm) 迭代器(iterator) 
//容器和算法之间通过迭代器进行无缝连接。 
//STL 几乎所有的代码都采用了模板类或者模板函数




/*STL六大组件*/
//STL大体分为六大组件,分别是:容器、算法、迭代器、仿函数、适配器(配接器)、空间配置器
	//1. 容器:各种数据结构,如vector、list、deque、set、map等, 用来存放数据。 
	//2. 算法:各种常用的算法,如sort、?nd、copy、for_each等 
	//3. 迭代器:扮演了容器与算法之间的胶合剂。 
	//4. 仿函数:行为类似函数,可作为算法的某种策略。 
	//5. 适配器:一种用来修饰容器或者仿函数或迭代器接口的东西。 
	//6. 空间配置器:负责空间的配置与管理。



/*STL中容器、算法、迭代器*/
//容器:置物之所也
//STL容器就是将运用广泛的一些数据结构实现出来 常用的数据结构:数组, 链表, 树, 栈, 队列, 集合, 映射表 等
//这些容器分为序列式容器和关联式容器两种 :
	//序列式容器:强调值的排序,序列式容器中的每个元素均有固定的位置。 
	//关联式容器 : 二叉树结构,各元素之间没有 严格的物理上的顺序关系

//算法:问题之解法也
//有限的步骤,解决逻辑或数学上的问题,这一门学科我们叫做算法(Algorithms)
//算法分为 : 质变算法和非质变算法。
	//质变算法:是指运算过程中会更改区间内的元素的内容。例如拷贝,替换,删除等等
	//非质变算法:是指运算过程中不会更改区间内的元素内容,例如查找、计数、遍历、寻找极值等等

//迭代器:容器和算法之间粘合剂
//提供一种方法,使之能够依序寻访某个容器所含的各个元素,而又无需暴露该容器的内部表示方式。
//每个容器都有自己专属的迭代器
//迭代器使用非常类似于指针,初学阶段我们可以先理解迭代器为指针
//迭代器种类:
	//输入迭代器 对数据的只读访问 只读,支持++、==、!=
	//输出迭代器 对数据的只写访问 只写,支持++
	//前向迭代器 读写操作,并能向前推进迭代器 读写,支持++、 == 、! =
	//双向迭代器 读写操作,并能向前和向后操作 读写,支持++、--,
	//随机访问迭代器 读写操作,可以以跳跃的方式访问任意数据,功能最强的迭代器 读写,支持++、--、[n]、 - n、<、 <= 、>、 >=
//常用的容器中迭代器种类为双向迭代器,和随机访问迭代器




/*容器算法迭代器初识*/
//STL中最常用的容器为Vector,可以理解为数组





/*vector存放内置数据类型*/
//需要包含头文件#incude<vector>
//容器:vector
//算法:for_each
//迭代器: vector<int>::iterator

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

void test23()
{
	//创建一个vector容器,认为是一个数组
	vector<int> v;

	//向容器中插入数据,通过.push_back(数据)进行尾插
	v.push_back(10);
	v.push_back(20);
	v.push_back(30);
	v.push_back(40);

	//通过迭代器访问容器中的数据
	vector<int>::iterator itbegin = v.begin();  //.begin()是起始迭代器,指向容器中第一个元素,本质是指针
	vector<int>::iterator itend = v.end();  //.end()是结束迭代器,指向容器中最后一个元素的下一个位置,本质是指针

	//第一种遍历方式
	while (itbegin != itend)
	{
		cout << *itbegin << endl;//解引用取出数据
		itbegin++;
	}

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

	//第三种遍历方式
	//利用STL提供的遍历算法,需要包含头文件#include<algorithm>
	for_each(v.begin(), v.end(), myPrint_vector);//只写函数名就可以,不用加括号
}




/*vector存放自定义数据类型*/

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

public:
	int m_Age;
	string m_Name;
};

void test24()
{
	vector<Person_v> v;

	Person_v p1("a", 1);
	Person_v p2("b", 1);
	Person_v p3("c", 1);
	Person_v p4("d", 1);
	Person_v p5("e", 1);

	//向容器中添加数据
	v.push_back(p1);
	v.push_back(p2);
	v.push_back(p3);
	v.push_back(p4);
	v.push_back(p5);

	//遍历容器中的数据 通过(*it).对姓名和年龄进行取值,或者通过it->进行取值
	//for里的<>里是啥数据类型,it就是指向什么数据类型的指针,这里it是一个Person_v的指针
	//通过(*it).可以取出Person_v里的成员属性或者通过it->得到成员属性
	for (vector<Person_v>::iterator it = v.begin(); it != v.end(); it++)
	{
		cout << "姓名:" << (*it).m_Name << "\t年龄:" << (*it).m_Age << endl;
		cout << "姓名:" << it->m_Name << "\t年龄:" << it->m_Age << endl;

	}
}

//存放自定义数据类型的指针
void test25()
{
	vector<Person_v*> v;

	Person_v p1("a", 1);
	Person_v p2("b", 1);
	Person_v p3("c", 1);
	Person_v p4("d", 1);
	Person_v p5("e", 1);

	//向容器中添加数据
	v.push_back(&p1);
	v.push_back(&p2);
	v.push_back(&p3);
	v.push_back(&p4);
	v.push_back(&p5);

	//遍历容器
	//for里面的<>是什么数据类型,it就是指向什么数据类型的指针,这里it是一个Person_v*的指针(指针的指针)
	//通过(*it)可以得到Person_v的指针,再通过(*(*it)).或者(*it)->可以得到成员属性
	for (vector<Person_v*>::iterator it = v.begin(); it != v.end(); it++)
	{
		cout << "---------------------------" << endl;
		cout << "姓名:" << (*(*it)).m_Name << "\t年龄:" << (*(*it)).m_Age << endl;
		cout << "姓名:" << (*it)->m_Name << "\t年龄:" << (*it)->m_Age << endl;
	}


}




/*vector容器嵌套容器*/
void test26()
{
	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 + 1);
		v2.push_back(i + 2);
		v3.push_back(i + 3);
		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++)
	{
		//it是指向容器的指针,(*it)是容器
		for (vector<int>::iterator vit = (*it).begin(); vit != (*it).end(); vit++)
		{
			cout << *vit << endl;
		}
		cout << endl;
	}
}




/*string基本概念*/
//本质:
	//string是C++风格的字符串,而string本质上是一个类 
//string和char * 区别:
	//char * 是一个指针 string是一个类,类内部封装了char*,管理这个字符串,是一个char*型的容器。
//特点:
	//string 类内部封装了很多成员方法
	//例如:查找find,拷贝copy,删除delete 替换replace,插入insert 
	//string管理char*所分配的内存,不用担心复制越界和取值越界等,由类内部进行负责





/*string构造函数*/
//构造函数原型:
	//string();   //创建一个空的字符串 例如: string str; 
	//string(const char* s); //使用字符串s初始化 
	//string(const string& str); //使用一个string对象初始化另一个string对象 
	//string(int n, char c);   //使用n个字符c初始化 
void test27()
{
	string s1;//默认构造
	const char *str = "Hello world!";
	string s2(str);//使用字符串初始化
	cout << "s2=" << s2 << endl;
	string s3(s2);//拷贝构造,使用一个string对象初始化另一个string
	cout << "s3=" << s3 << endl;
	string s4(10, 'a');//10个a
	cout << "s4=" << s4 << endl;
}




/*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赋给当前字符串

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

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

	string str3;
	str3 = '3';
	cout << "str3=" << str3 << endl;

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

	string str5;
	str5.assign("helloworld", 5);
	cout << "str5=" << str5 << endl;

	string str6;
	str6.assign("Hello C++");
	cout << "str6=" << str6 << endl;

	string str7;
	str7.assign(10, 'w');//10个w
	cout << "str7=" << str7 << endl;
}

//总结:
 //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个字符连接到字符串结尾

void test29()
{
	string str1 = "我";
	str1 += "爱学习";
	cout << "str1=" << str1 << endl;

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

	string str2 = "数学、C++";
	str1 += str2;
	cout << "str1=" << str1 << endl;

	string str3 = "I";
	str3.append(" love");
	cout << "str3=" << str3 << endl;

	str3.append(" game abcf", 5);
	cout << "str3=" << str3 << endl;

	str3.append(str2);
	cout << "str3=" << str3 << endl;

	string str4 = "helloworld";
	str3.append(str4, 5, 5);
	cout << "str3=" << str3 << endl;

}






/*string查找和替换*/
//功能描述:
	//查找:查找指定字符串是否存在 
	//替换:在指定的位置替换字符串
//函数原型:
//int find(const string& str, int pos = 0) const; //查找str第一次出现位置,从pos开始查找 
//int find(const char* s, int pos = 0) const; //查找s第一次出现位置,从pos开始查找 
//int find(const char* s, int pos, int n) const; //从pos位置查找s的前n个字符第一次位置 
//int find(const char c, int pos = 0) const; //查找字符c第一次出现位置 
//int rfind(const string& str, int pos = npos) const; //查找str后一次位置,从pos开始查找 
//int rfind(const char* s, int pos = npos) const; //查找s后一次出现位置,从pos开始查找 
//int rfind(const char* s, int pos, int n) const; //从pos查找s的前n个字符后一次位置 
//int rfind(const char c, int pos = 0) const; //查找字符c后一次出现位置 
//string& replace(int pos, int n, const string& str); //替换从pos开始n个字符为字符串str 
//string& replace(int pos, int n,const char* s);//替换从pos开始的n个字符为字符串s

void test30()
{
	string str1 = "abcdefgde";
	int pos = str1.find("df");

	cout << "pos=" << pos << endl;

	if (pos == -1)
	{
		cout << "未找到字符串" << endl;
	}
	else
	{
		cout << "找到字符串" << endl;
	}
	//rfind和find的区别
		//rfind从右往左查找;find从左往右查找
	pos = str1.rfind("de");
	cout << "pos=" << pos << endl;
}

void test31()
{
	string str1 = "abcdefg";

	//从1号位置起,3个字符替换为“1111”
	str1.replace(1, 3, "1111");
	cout << "str1=" << str1 << endl;
}
//总结:
	//find查找是从左往后,rfind从右往左 
	//find找到字符串后返回查找的第一个字符位置,找不到返回 - 1 
	//replace在替换时,要指定从哪个位置起,多少个字符,替换成什么样的字符串






/*string字符串比较*/
//功能描述:
	//字符串之间的比较
//比较方式:
	//字符串比较是按字符的ASCII码进行对比
	//= 返回 0 
	//> 返回 1 
	//< 返回 - 1
//函数原型:
	//int compare(const string &s) const;//与字符串s比较 
	//int compare(const char *s) const;//与字符串s比较

void test32()
{
	string str1 = "hello";
	string str2 = "xello";

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

}

//总结:字符串对比主要是用于比较两个字符串是否相等,判断谁大谁小的意义并不是很大





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

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

	cout << "str=" << str << endl;

	for (int i = 0; i < str.size(); i++)//string.size()用来输出字符串长度
	{
		cout << str[i] << " " ;
		
	}
	cout << endl;

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

	//修改单个字符
	str[0] = 'x';
	cout << "str=" << str << endl;

	str.at(1) = 'x';
	cout << "str=" << str << endl;
}
//总结:string字符串中单个字符存取有两种方式,利用 [ ] 或 at




/*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个字符 

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

	//插入
	str.insert(1, "111");
	cout << "str=" << str << endl;//h111ello

	//删除
	str.erase(1, 3);
	cout << "str=" << str << endl;//hello
}
//总结:插入和删除的起始下标都是从0开始





/*string子串*/
//功能描述:
	//从字符串中获取想要的子串
//函数原型:
	//string substr(int pos = 0, int n = npos) const;//返回由pos开始的n个字符组成的字符串
void test35()
{
	string str = "abcdef";
	cout << "str=" << str << endl;
	string subStr = str.substr(1, 3);
	cout << "str=" << subStr << endl;
}

//实用操作
void test36()
{
	string email = "hello@sina.com";

	//从邮件地址中获取用户名信息
	int pos = email.find("@");

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

//总结:灵活的运用求子串功能,可以在实际开发中获取有效的信息





/*vector容器*/
/*vector基本概念*/
//功能:
	//vector数据结构和数组非常相似,也称为单端数组
//vector与普通数组区别: 
	//不同之处在于数组是静态空间,而vector可以动态扩展
	//静态空间容量是一定的,不能再扩展,比如原来有5个容量,那就只能装5个数
//动态扩展:
	//并不是在原空间之后续接新空间,而是找更大的内存空间,然后将原数据拷贝新空间,释放原空间
	//vector容器的迭代器是支持随机访问的迭代器





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







int main()
{
	/*string子串*/
	test35();
	test36();

	
	
	/*string插入和删除*/
	test34();
	
	
	
	
	/*string字符存取*/
	test33();


	
	
	/*string字符串比较*/
	test32();
	
	
	
	
	
	/*string查找和替换*/
	test30();
	test31();


	
	/*string字符串拼接*/
	test29();
	
	
	
	/*string赋值操作*/
	test28();
	
	
	
	
	/*string构造函数*/
	test27();
	
	
	
	/*vector容器嵌套容器*/
	test26();

	
	
	
	
	/*vector存放自定义数据类型*/
	test24();
	test25();
	
	
	
	/*vector存放内置数据类型*/
	test23();
	
	
	
	
	/*类模板案例*/
	test21();
	test22();


	
	
	/*类模板与友元*/
	test19();
	test20();

	
	
	
	/*类模板分文件编写*/
	test18();
	
	
	
	
	
	/*类模板成员函数类外实现*/
	test17();
	
	
	
	
	
	/*类模板与继承*/
	test15();
	test16();



	
	
	/*类模板对象做函数参数*/
	test12();
	test13();
	test14();


	
	/*类模板中成员函数创建时机*/
	test11();
	
	
	
	/*类模板与函数模板区别*/
	test09();
	test10();
	
	
	
	/*类模板语法*/
	test08();
	
	
	
	
	/*模板的局限性*/
	test06();
	test07();
	
	
	
	/*普通函数与函数模板的调用规则*/
	test05();//如果函数模板和普通函数都可以实现,优先调用普通函数
	
	

	
	/*普通函数与函数模板的区别*/
	test04();
	
	
	
	
	/*函数模板注意事项*/
	test03();
	
	
	
	/*函数模板语法*/
	test01();
	
	
	
	
	
	system("pause");

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值