一、模板 (template)

1.1、模板函数

1> 语法格式:

泛型编程
1. 函数不用确定其处理的数据类型
2. 把数据类型作为参数传递
3. 泛型编程的语法规则
template 的含义是开始泛型编程

< typename T >的含义是声明数据类型叫T 
template <typename 模板类型1, typename 模板类型2, ...>
返回类型 函数名(形参表){
	函数体;   // 函数中的类型可以使用模板类型替换
}

2> 模板函数的调用

函数名<模板实参类型表>(实参表);
	
顺口溜:尖找尖,圆找圆。

1.2、模板类

1> 语法格式

template <typename 模板类型1, ...>
class 类名:继承表{
	成员变量;
	成员函数;
};

2> 模板类的使用

类名<模板实参类型1,...> 对象名(实参表);

类名<模板实参类型1,...> 对象名 = 类名<模板实参类型1,...>(实参表);
	
类名<模板实参类型1,...> *对象指针 =	new 类名<模板实参类型1,...>(实参表);
	
类名<模板实参类型1,...> &引用名 = 引用的目标;

3> 编译器编译模板函数/模板类的实现的机制?

采用是二次编译或者延时编译

当编译器第一次“看到”模板函数定义时,由于模板函数的参数尚不明确,编译器只做与类型无关的一般性检查,如果检查编译无误则生成模板的内部表现形式。
	
当编译器第二次“看到”模板函数被调用时,再次使用模板函数调用时传递的类型实参,结合模板函数的内部表现形式,做与类型相关的语法检查,如果无误则生成具体的二次编译的代码。

4> C 模板

#include <stdio.h>
#if 0
int add_int(int x, int y)
{
	return x > y ? x : y;
}
float add_int(float x, float y)
{
	return x > y ? x : y;
}
#endif 
// 宏定义中不允许出现换行,如果右换行需要加续行符"\"
// ## : 拼接
// int max_int(int x, int y){} 
#define  MAX(T)  T max_##T(T x, T y)  \
		{   \
			return x > y ? x : y;  \
		}
MAX(int)
MAX(short)
MAX(double)

#define max(T)  max_##T

// void min_int(int *min, int x, int y){}
#define  MIN(T0, T1, T2)  T0 min_##T2(T1 min, T2 x, T2 y) \
		{  \
			 *min = x > y ? y : x;  \
		}

MIN(void, int *, int)
MIN(void, double *, double)

#define min(T)  min_##T

int main(int argc, const char *argv[])
{
	int a = 100, b = 200;
	printf("max_int = %d\n", max_int(a, b));
	int min;
	min_int(&min, a, b);
	printf("min_int = %d\n", min);
	
	double d1 = 3.14, d2 = 5.67;
	printf("max_double = %lf\n", max_double(d1,d2));
	double mind;
	min(double)(&mind, d1, d2);
	
	short s1 = 20, s2 = 6;
	                         //max_short(s1,s2)
	printf("max_short = %d\n", max(short)(s1,s2));

	return 0;
}
// 结果:
max_int = 200
min_int = 100
max_double = 5.670000
max_short = 20
#include <stdio.h>

// 宏定义的最后一个表达是就是宏定义的返回值
#define MAX(T, a, b)   ({T tmp,retVal; if (a > b) tmp = a; else tmp = b; retVal = tmp;}) 
#define MIN(a, b)  ({a>b ? b : a;}) 

int main(int argc, const char *argv[])
{
	printf("max = %d\n", MAX(int, 100,200));
	printf("min = %d\n", MIN(100,200));
	
	return 0;
}

5> C++ 模板

#include <iostream>
using namespace std;
// 交换两个变量的值 

template <typename A>
void Swap(A& a, A& b){
	A tmp;
	tmp = a;
	a = b;
	b = tmp;
}

int main(int argc, const char *argv[])
{
	int x = 100, y = 200;
	Swap<int>(x, y);
	cout << "x = " << x << ", y = " << y << endl; 

	string str1 = "zhou";
	string str2 = "kai";
	Swap<string> (str1, str2);
	cout << str1 << " " << str2 << endl;
	return 0;
}
// 结果:
x = 200, y = 100
kai zhou
#include <iostream>
using namespace std;
template <typename A>
class Shape{
public:
	Shape(A x, A y):m_x(x),m_y(y){}
	~Shape(void){}
	void Painter(void){
		cout << "坐标" << m_x << ":" << m_y << endl;
	}
private:
	A m_x;
	A m_y;
};

int main(int argc, const char *argv[])
{
	Shape <int> S1(10,20);
	S1.Painter();

	Shape <float> *S2 = new Shape<float>(11.1, 12.1);
	S2->Painter();	
	
	Shape <double> S3 = Shape<double>(111.11,222.22);
	Shape <double> &S4 = S3;
	S4.Painter();

	return 0;
}
//结果:
坐标10:20
坐标11.1:12.1
坐标111.11:222.22
#include <iostream>
using namespace std;
template <typename A>
class Shape{
public:
	Shape(A x, A y):m_x(x),m_y(y){}
	virtual ~Shape(void){}
	virtual void Painter(void){
		cout << "坐标" << m_x << ":" << m_y << endl;
	}
private:
	A m_x;
	A m_y;
};
template <typename A, typename B>
class Rect:public Shape<A>{
public:
	Rect(A x, A y, B w, B h):Shape<A>(x, y),m_w(w),m_h(h){}
	~Rect(void){}
	void Painter(void){
		Shape<A>::Painter();
		cout << "宽高:" << m_w<< ":" << m_h << endl; 
	}
private:
	B m_w;
	B m_h;
};
template <typename A, typename B>
class Circle:public Shape<A>{
public:
	Circle(A x, A y, B r):Shape<A>(x,y),m_r(r){}
	virtual ~Circle(){}
	virtual void Painter(void){
		Shape<A>::Painter();
		cout << "半径:" << m_r << endl;
	}
private:
	B m_r;
};

int main(int argc, const char *argv[])
{
	Shape<int> *S1 = new Rect<int, float>(1,2,3.4,5.6);
	S1->Painter();

	Circle <int, double> C1(5,6,3.14);
	Shape<int> &S2 = C1;
	S2.Painter();

	return 0;
}
//结果:
坐标1:2
宽高:3.4:5.6
坐标5:6
半径:3.14

笔试题:编译器编译模板函数/模板类的实现机制?

采用是二次编译或者延时编译

当编译器第一次“看到”模板函数定义时,由于模板函数的参数尚不明确,编译器只做与类型无关的一般性检查,如果检查编译无误则生成模板的内部表现形式。

当编译器第二次“看到”模板函数被调用时,再次使用模板函数调用时传递的类型实参,结合模板函数的内部表现形式,做与类型相关的语法检查,如果无误则生成具体的二次编译的代码。
(及具体函数的代码)。

二、基于模板C++实现的标准模板库

STL(Standed Template Library)

2.1、容器、迭代器、泛型算法

容器:模板化的数据结构
泛型算法:模块化的算法 
迭代器:为不同类型的容器,提供了
相同的数据访问的接口函数。

2.2、容器 (模板类)

vector (向量)
list(列表)

2.3、迭代器

1> 根据迭代器的特性:单向迭代器和双向迭代器
2> 根据迭代器的方向:正向迭代器和反向迭代器
3> 根据迭代器的运算:顺序迭代器和随机迭代器
4> 根据对目标的访问:可写迭代器和只读迭代器
	
每一种容器都会以嵌套的形式提供至少四种迭代器
iterator - 正向可写迭代器
const_iterator - 正向只读迭代器
reverse_iterator - 反向可写迭代器
const_reverse_iterator - 反向只读迭代器

2.4、泛型算法

排序,查找,组合

三、vector(向量)

3.1、特点

连续的内存空间,通过下标访问
动态内存管理

3.2、实例化对象

#include <vector>
using namespace std;
	
A.vector<元素类型> 向量对象; // 空向量
vector<int> vi;
cout << vi.size () << endl; // 0
cout << sizeof (vi) << endl; // 12
容器大小:元素个数,size()
容器对象大小:容器本身的字节数,sizeof()
	
B.vector<元素类型> 向量对象 (初始大小); // 非空向量
vector<int> vi (3);
基本类型:初始化为"零"
类类型:用缺省构造函数初始化

C.vector<元素类型> 向量对象 (初始大小, 初始值);
vector<int> vi (3, 5); // 5 5 5
vector<Student> vs (3, Student ("张飞", 22));
	
D.vector<元素类型> 向量对象 (起始迭代器, 终止迭代器);
int ai[5] = {10, 20, 30, 40, 50};
vector<int> v1 (ai, ai + 5); // 10 20 30 40 50
vector<int> v2 (&ai[0], &ai[5]); // 10 20 30 40 50
vector<int> v3 (ai+1, ai + 4); // 20 30 40
		
在STL中但凡用两个迭代器表示一个元素范围时,
其中的终止迭代器一定是该范围中最后一个元素的下一个位置。
#include <iostream>
#include <vector>
using namespace std;
void print(vector<int> const& v){
	cout << "对象大小:" << sizeof(v) << endl;
	size_t size = v.size(); // 向量的容量
	cout << "向量容量:" << size << endl;
	cout << "变量向量中的元素:";
	for(int i = 0; i < size; i++)
		cout << v[i] << " ";
	cout << endl;
}

int main(int argc, const char *argv[])
{
	vector<int> v1; // 空向量
	print(v1);
	cout << "---------------" << endl;
	vector<int> v2(3); // 定义向量对象,大小为3
	print(v2);
	cout << "---------------" << endl;
	// 定义向量对象,大小为3,初始值为5
	vector<int> v3(3,5);
	print(v3);
	cout << "---------------" << endl;
	v3.reserve(5);
	print(v3);
	v3.push_back(6); // 插入元素6
	print(v3);
	v3.push_back(7); // 插入元素7
	print(v3);
	cout << "---------------" << endl;
	
	int arr[5]={10,20,30,40,50};
	vector<int> v4(arr,arr+5);
	print(v4);

	vector<int> v5(&arr[0], &arr[5]);
	print(v5);

	vector<int> v6(arr+2, arr+4);
	print(v6);


	return 0;
}

//结果:
对象大小:24
向量容量:0
变量向量中的元素:
---------------
对象大小:24
向量容量:3
变量向量中的元素:0 0 0 
---------------
对象大小:24
向量容量:3
变量向量中的元素:5 5 5 
---------------
对象大小:24
向量容量:3
变量向量中的元素:5 5 5 
对象大小:24
向量容量:4
变量向量中的元素:5 5 5 6 
对象大小:24
向量容量:5
变量向量中的元素:5 5 5 6 7 
---------------
对象大小:24
向量容量:5
变量向量中的元素:10 20 30 40 50 
对象大小:24
向量容量:5
变量向量中的元素:10 20 30 40 50 
对象大小:24
向量容量:2
变量向量中的元素:30 40 

四、迭代器

A.随机迭代器:
在顺序迭代器的基础上又增加了如下功能:可以和整数做加减法运算;同类型的迭代器之间可以做大小比较运算和相减运算。

B.八个特征迭代器
	iterator begin (void) 
		返回一个迭代器,该迭代器引用向量的第一个元素
	iterator end (void)
		返回一个迭代器,该迭代器位于向量的最后一个元素之后
	reverse_iterator rbegin (void)
		返回引用向量中最后一个元素的迭代器
	reverse_iterator rend (void)
		返回位于向量中第一个元素之前的迭代器
	const_iterator begin (void) const
		返回一个迭代器,该迭代器引用向量的第一个元素
	const_iterator end (void) const
		返回一个const 迭代器,该迭代器位于向量的最后一个元素之后
	const_reverse_iterator rbegin (void) const
		返回引用向量中最后一个元素的迭代器
	const_reverse_iterator rend (void) const
		返回位于向量中第一个元素之前的迭代器
	begin - 起始
	end - 终止
	不带const - 可写
	带const - 只读
	不带r - 正向
	带r - 反向

对于正向迭代器而言,起始位置在首元素,终止位置在尾元素之后;
对于反向迭代器而言,起始位置在尾元素,终止位置在首元素之前。

任何可能导致容器结构发生变化的操作,比如数据元素的增删,都可能引发其内存布局的调整,
因此操作之前所获得的迭代器就会因为这种调整而失效,安全起见最好重新定位这个迭代器以后再继续使用。	
#include <iostream>
#include <vector>
using namespace std;

void print(vector<int> const& v){
	// 使用正向迭代器遍历容器中的所有元素
	for(vector<int>::const_iterator it = v.begin();
			it != v.end(); ++it)
		cout << *it << " ";
	cout << endl;

}

void rprint(vector<int> const& v){
	for(vector<int>::const_reverse_iterator 
			it = v.rbegin(); it != v.rend();
			it++){
		cout << *it << " ";
	}
	cout << endl;
}
int main(int argc, const char *argv[])
{
	// 空向量
	vector<int> v1;
	for(int i = 1;i<=10;i++)
		v1.push_back(i*10);
	print(v1);
	rprint(v1);
	
	return 0;
}
//结果:
10 20 30 40 50 60 70 80 90 100 
100 90 80 70 60 50 40 30 20 10 

五、算法

排序
IT sort (IT begin, IT end);
对[begin,end)区间内的元素做快速排序。	
<algorithm>头文件中的全局函数sort()只能应用于带有随机迭代器的连续内存容器,

比如vector、deque。
列表的排序必须使用其成员函数sort()。
#include <iostream>
#include <algorithm> // 算法相关的头文件
#include <vector>
using namespace std;

template <typename T1>
void print(T1 begin, T1 end){
	while(begin != end)
		cout << *begin++ << " ";
	cout << endl;
}


int main(int argc, const char *argv[])
{
	vector<int> v1;
	v1.push_back(33);
	v1.push_back(57);
	v1.push_back(78);
	v1.push_back(25);
	v1.push_back(123);
	// 编译器可以完成隐式类型的推导
	print (v1.begin(), v1.end());
	// 升序排序
	sort(v1.begin(), v1.end());
	print (v1.begin(), v1.end());

	return 0;
}
//结果:
33 57 78 25 123 
25 33 57 78 123 

六、列表(list)

6.1、特点

双向链表 
在内存中是不连续的

6.2、实例化

list() 声明一个空列表;
list(n) 声明一个有n个元素的列表,每个元素都是由其默认构造函数T()构造出来的
list(n,val) 声明一个由n个元素的列表,每个元素都是由其复制构造函数T(val)得来的
list(n,val) 声明一个和上面一样的列表
list(first,last) 声明一个列表,其元素的初始值来源于由区间所指定的序列中的元素

6.3、唯一化

void unique (void);
仅对容器中连续的重复出现的元素做唯一化,而不是对整个容器中的元素做唯一化。
10 10 20 20 10 20 30 20 20 10 10
						| unique()
						V
10       20      10 20 30 20      10

6.4、排序

void sort (void); // 用元素类型的"<"运算符比较大小
<algorithm>头文件中的全局函数sort()只能应用于带有随机迭代器的连续内存容器,
比如vector、deque。列表的排序必须使用其成员函数sort()。

6.5、拆分:调用列表.splice (目标迭代器, 参数列表, ...);

从参数列表中剪切出全部或者部分元素,插入到调用列表中目标迭代器之前。
void splice (IT pos, list& lst); // 剪切全部
void splice (IT pos, list& lst, IT del); // 剪切一个
void splice (IT pos, list& lst, IT begin, IT end); //剪切区间

6.6、合并:将两个有序列表合并为一个,仍然保持有序。

void merge (list& lst); // <
参见:04list.cpp
#include <iostream>
#include <algorithm>
#include <list>

using namespace std;
template <typename T1>
void print(T1 begin, T1 end)
{
	while(begin != end)
		cout << *begin++ << " " ;
	cout << endl;
}

int main(int argc, const char *argv[])
{
	int arr[10] = {10,10,20,20,40,50,50,30,10,10};
	list<int> l1(arr, arr+10);
	// 连续重复的元素唯一化
	l1.unique();
	print(l1.begin(), l1.end());
	
	// 调用list中的成员函数sort进行排序
	l1.sort();
	print(l1.begin(), l1.end());
	
	l1.unique();
	print(l1.begin(), l1.end());
	
	cout << "拆分" << endl;
	
	list <int> l2;
	l2.push_back(111);
	l2.push_back(555);
	l2.push_back(333);
	l2.push_back(444);
	l2.push_back(222);
	
	list<int>::iterator pos = l1.begin();
	/*
	l1.splice(pos,l2); 剪切全部
	*/
	/*
	// 剪切一个元素
	list<int>::iterator del = l2.begin();
	l1.splice(pos, l2, ++del);
	*/
	print(l1.begin(), l1.end());
	
	cout << "合并" << endl;
	l1.merge(l2);
	l1.sort();
	print(l1.begin(), l1.end());

	return 0;
}
//结果:
10 20 40 50 30 10 
10 10 20 30 40 50 
10 20 30 40 50 
拆分
10 20 30 40 50 
合并
10 20 30 40 50 111 222 333 444 555 
posted on 2020-12-19 21:07  八杯水  阅读(131)  评论(0)    收藏  举报