网络知识 娱乐 c++模板的概念全新解释

c++模板的概念全新解释

文章目录

  • 前言
  • 一、模板的概念
      • 强类型的严格性和灵活性
      • 解决冲突的路径
      • 模板的概念
    • 一(1).函数模板
            • 函数模板的定义
            • 函数模板的实例化
      • 函数模板的重载
            • 1.函数模板的重载
            • 2.用普通函数重载函数模板
            • 3.用特定函数重载函数模板
    • 类模板
            • 类模板定义
            • 类模板的实例化
            • 使用函数类型参数的类模板
            • 使用默认参数的类模板
    • 标准模板库STL
          • 容器
            • 容器的分类
            • 顺序容器和关联容器
            • 迭代器
            • 迭代器的分类
            • 迭代器的使用
          • 算法
            • 通用算法的调用形式
            • 通用算法应用


前言

我们都知道在c++与c语言的区别中,最大的不同就是c++訲多了对象和模板,前面我们介绍了面向对象的编程,现在我们来认识一下c++中模板的知识。
模板八函数或类要处理的数据类型参数化,即表现为数据的多态性。在面向对象技术中,模板是另一种代码重用机制。


提示:以下是本篇文章正文内容,下面案例可供参考

一、模板的概念

强类型的严格性和灵活性

在强类型程序设计语言中,参与运算的所有对象的类型在编译时即可确定下来,并且编译程序将进行严格的类型检查,这样就可以在程序未运行之前就检查出类型不兼容的错误。
但是由于这种强类型语言在提高程序的可靠性的同时又带来了一些负作用。
例如我们在使用函数求某一个计算类容的时候,会出现参数类型不一样,他们的算法相同,但是参数不同,所以程序员就必须写两个程序。这就不利于程序的扩展和维护。

解决冲突的路径

如何解决强类型的严格性与灵活性的冲突?以往有三种方法。
第一种:利用宏函数,使用宏函数虽然确实会简便问题,但是我们知道引入宏函数后可能会导致程序出现以些意想不到的问题。所以在c++中一般不会使用宏函数。
第二种:为各种类型重载这一函数,但是重载函数又显得麻烦。增加工作量。
第三种:放松类型检查,在编译期间忽略这些类型的匹配问题。而在运行期间进行类型匹配检查,但是在程序运行的时候可能会出现类型不兼容的错误。
综和上面的方法,我们发现无论何种方法,都会有漏洞,那么最理想的方法是什么?最理想的方法是直接将数据类型作为类的参数,就像函数可以将数据作为参数一样。这种机制叫做“类属”(genericity),强类型程序设计语言(如Ada,Eiffel等语言)通常都采用这种方式

模板的概念

模板是一种参数化多态性的工具,可以为逻辑功能相同而类型不同的程序提供代码共享的机制。
由于c+程序结构的主要构件是类和函数,所以在c++中,模板被分为函数模板和类模板。模板并非一个实实在在的函数或类,仅仅是函数或类的描述,模板运算对象的类型不是实际的数据类型,而是一种参数化的类型(又称为类属类型)。带类属参数的函数叫做函数模板,带类属参数的类叫做类模板。
在这里插入图片描述

一(1).函数模板

通常,设计的算法是可以处理多种数据类型的,c++提供的函数模板可以一次定义出具有共性(除类型参数外,其余全相同)的一组函数,同时也可以处理多种不同类型数据的函数。从而大大增强了函数设计的通用性。

函数模板的定义

函数模板定义的格式:
template
()
{

}
其中,关键字template是定义模板的关键字,中包含一个或多个用逗号分开的模板参数项,每一项又保留字class或者typename开始,后跟用户命名的标识符,此标识符为模板参数,表示数据类型。函数模板中可以利用这些模板参数定义函数返回值类型,参数类型和函数体中的变量类型。它同基本数据类型一样,可以在函数中的任何地方使用。
在这里插入图片描述

函数模板的实例化
#include
#include
using namespace std;
template <typename T>  //函数模板;
T Max(T a, T b)
{
	return a > b ? a : b;
}
int main()
{
	int a, b;
	cout << "Input two integers to a&b:" << endl;
	cin >> a >> b;
	cout << "max(" << a << "," << b << ")=" << Max(a, b) << endl;        //有些编译软件不能使用max模板函数,是由于系统自带max函数;
	char c, d;
	cout << "Input two chars to c&d:" << endl;
	cin >> c >> d;
	cout << "max(" << "'" << c << "'" << "," << "'" << d << "'" << ")=";
	cout<< Max(c, d) << endl;
	float x, y;
	cin >> x >> y;
	cout << "Input two float x&y:" <<endl;
	cout << "max(" << x << "," << y << ")=" << Max(x, y) << endl;
	cout << "Input two string to p&h:" << endl;
	string p, h;
	cin >> p >> h;
	cout << "max(" << """ << p << """ << "," <<""" << h <<""" << ")=";
	cout << Max(p, h) << endl;
	return 0;
}

在这里插入图片描述
在程序中,我们可以发现在使用相同的函数时,不再需要多次定义数据类型,编译器会根据语句中的实际数据类型而产生相应的函数。
这就极大的减轻了程序员的工作负担。
下面我们来看看他的图示:
在这里插入图片描述
函数模板的声明和定义必须是全局作用域,而且模板不能被声明成类的成员函数。

函数模板的重载

函数模板有多种重载方式,可以定义同名的函数模板,提供不同的参数和实现;也可以用其他非模板函数重载。

1.函数模板的重载

重载函数模板和重载函数类似,重载函数模板便于定义类属参数,或者函数参数的类型。个数不同所进行的操作。

#include
using namespace std;
template <typename T>
T Max(T a, T b)
{
	return a > b ? a : b;
}
//重载;
template<typename T>
T Max(T a[], T n)
{
	T temp;
	temp = a[0];
	for (int i = 0; i < n; i++)
	{
		if (temp < a[i])
			temp = a[i];
	}
	return temp;
}
int main()
{
	int a, b;
	cout << "Input two integers to a&b" << endl;
	cin >> a >> b;
	cout << "Max(" << a << "," << b << ")=" << Max(a, b) << endl;
	int i, aa[10] = { 3,7,9,11,0,6,7,5,4,2 };
	cout << "The original array:" << endl;
	for (i = 0; i < 10; i++)
	{
		cout << aa[i] << ",";
	}
	cout << endl;
	cout << "Max of 10 integers is" << Max(aa, 10) << endl;
	return 0;
}
2.用普通函数重载函数模板

函数模板实例化时,实际参数类型将替换模板参数,虽然这种参数替换具有类型检查功能,但是却没有普通传值参数的类型转换机制。
也就是说,函数模板没办法预知隐式的类型转换。这个时候我们就可以用非模板函数重载一个同名的函数模板;

#include
using namespace std;
template <typename T>
T Max(T a,T b)
{
	return a > b ? a : b;
}
int Max(int a, float b)            //用普通函数重载函数模板;
{
	return a > b ? a : b;
}
int main()
{
	char a = '4', b = '5';
	float c = 5.7;
	int d = 10;
	cout<<Max(a, b) << endl;
	cout << Max(d, c) << endl;
	return 0;
}
3.用特定函数重载函数模板
#include
#include
using namespace std;
template <typename T>
T Max(T a, T b)
{
	return a > b ? a : b;
}

int Max(int a, float b)
{
	return a > b ? a : b;
}
char* Max(char* a,char* b)
{
	return strcmp(a, b) > 0 ? a : b;
}
int main()
{
	char a = '4', b = '5';
	int c = 5;
	cout << "Max(" << "'" << a << "'" << "," << "'" <<b << "'" << ")=";
	cout << Max(a, b) << endl;
	cout << "Max(" << "'" << a << "'" << "," << "'" << c << "'" << ")=" << Max(a, c) << endl;
	const char* p, * h;
	p = "qaz";
	h = "wsx";
	cout << "Max(" << """ << p << """ << "," << """ << h << """ << ")=" << Max(p,h) << endl;
}

类模板

同函数模板一样,使用类模板可以为类定义一种模式,使得类中的某些数据成员。某些成员函数的参数以及某些成员函数的返回值能取任意类型。类模板是对一批仅有成员数据类型不同类的抽象,程序员只要为这一批类所组成的整个类家族创建一个类模板,然后给出一套程序代码,就可以用来生成多种具体的类,即模板类。

类模板定义

类是对一组对象的公共性质的抽象,而类模板则是对不同类的公共性质的抽象,英雌类模板属于是更高层次的抽象。
格式:
template
class
{

}
其中,中包含一个或多个用逗号隔开的类型,参数项可以包含数据类型,也可以包含类属类型,如果是类属类型,则必须加class或者typename做前缀。
类模板中的成员函数的定义:
template
返回值类型::(参数表)
{
函数体
}
既是类模板中定义中的类型形式参数表中的参数名。
类模板中的成员函数放在类模板的外部的时候,也可以在前面加inline表明内连函数函数。

类模板的实例化

在这里插入图片描述
与函数模板不同的是,函数模板的实例化是由编译系统在处理函数调用时自动完成,而类模板的实例化必须由程序员在程序中显示的指定。

在这里插入图片描述
代码演示:

#include
#include
using namespace std;
const int size = 10;
template <typename AType>
class atype
{
public:
	atype()
	{
		int i;
		for (i = 0; i < size; i++)
		{
			array[i] = i;
		}
	}
	AType& operator[](int n);
private:
	AType array[size];
};
template <typename AType>
AType& atype<AType>::operator[](int n)
{
	if (n<0 || n>size)
	{
		cout << "下标" << n << "超出范围!" << endl;
		exit(1);
	}
	return array[n];
}
int main()
{
	const int size = 10;
	atype<int>intob;
	atype<double>doubleob;
	int i;
	cout << "Integer 数组:";
	for (i = 0; i < size; i++)
		intob[i] = i;
	for (i = 0; i < size; i++)
	{
		cout << intob[i] << " ";
	}
	cout << endl;
	cout << "Double 数组:";
	for (i = 0; i < size; i++)
	{
		doubleob[i] = (double)i / 2;
	}
	for (i = 0; i < size; i++)
	{
		cout << doubleob[i] << " ";
	}
	cout << endl;
	intob[12] = 100;         //下标越界;
	return 0;
}

在这里插入图片描述
类模板的实例化创建的结果是一种类型,而类的实例化创建的结果则是一个对象。
在这里插入图片描述

使用函数类型参数的类模板

在类模板的《模板参数表中》,必须至少有一个类参数,当然也可以有多个类参数。还可以有非类参数的参数,这样的参数一般称之为函数类型参数,也可以称之为无类型模板参数。
函数参数类型只限于整型,指针和引用,其他的类型(例如浮点型float)则不能使用。传递给函数参数类型的实参要么是整型常量,要么由指向全局函数或对象的指针或引用组成。由于函数参数类型的值不能改变,所以函数参数类型参数本身被看作常量,因此,函数类型参数可以用来设定数组大小。

#include
#include
using namespace std;
template <typename AType,int size>             //size作为函数参数类型参数
class atype          //模板类;
{
public:
	atype()
	{
		int i;
		for (i = 0; i < size; i++)
			array[i] = i;
	}
	AType& operator[](int n);    //"[]"运算符重载;至少含有一个参数;
private:
	AType array[size];
};
template <typename AType,int size>
AType& atype<AType, size>::operator[](int n)
{
	//下标越界检查;
	if (n<0 || n>size)
	{
		cout << "下标" <<n<<"超出范围" << endl;
		exit(1);
	}
	return array[n];
}
int main()
{
	//10个元素的integer数组类,intob为该类的一个对象;
	atype<int, 10>intob;
	//10个元素的double数组类,doublob为该类的一个对象
	atype<double, 10>doublob;
	int i;
	cout << "integer[]数组类: ";
	for (i = 0; i < 10; i++)
	{
		intob[i] = i;
		cout << intob[i] << "  ";
	}
	cout << endl;
	cout << "doublob[]数组类: ";
	for (i = 0; i < 10; i++)
	{
		doublob[i] = (double)i / 2;
		cout << doublob[i] << "  ";
	}
	cout << endl;
	intob[12] = 100;
	return 0;
}

在这里插入图片描述

使用默认参数的类模板

类模板可以包含于通用类型相关的默认参数。当类模板被实例化时,如果没有指定其他的数据类型,则使用默认数据类型。

#include
#include
using namespace std;
template<typename AType=int,int size=10>
class atype           //使用默认参数的模板类;
{
public:
	atype()
	{
		int i;
		for (i = 0; i < size; i++)
		{
			array[i] = i;
		}
	}
	AType& operator[](int n);
private:
	AType array[size];
};
template<typename AType,int size>
AType& atype<AType, size>::operator[](int n)
{
	if (n<0 || n>size)
	{
		cout << "下标" << n << "越界" << endl;
		exit(1);
	}
	return array[n];
}
int main()
{
	//12个元素的integer数组类,inton为模板类的一个对象;
	atype<int,12>intob;
	//double数组类,默认10个元素长度,
	atype<double>doubleob;
	//数组类,默认int型,默认10个元素长度;
	atype<>defaultob;
	int i;
	cout << "Integer 数组类:";
	for (i = 0; i < 12; i++)
	{
		intob[i] = i;
		cout << intob[i] << "  ";
	}
	cout << endl;
	cout << "double 数组类:";
	for (i = 0; i < 10; i++)
	{
		doubleob[i] = (double)i / 2;
		cout << doubleob[i] << "  ";
	}
	cout << endl;
	cout << "默认参数类型:";
	for (i = 0; i < 10; i++)
	{
		cout << defaultob[i] << "  ";
	}
	cout << endl;
	return 0;
}

在这里插入图片描述

标准模板库STL

c++中包含一个有许多组件的标准库。标准模板库(Standard Template Library,STL)是标准c++标准库的一部分。

容器

“容器”是数据结构,是包含对象的对象。例如,数组,队列,堆栈,树,图等数据结构中的每一个节点都是一个对象。这些结构按照某种特定的逻辑关系组合在一起,就成为了一个新的对象。

容器的分类

在这里插入图片描述
在这里插入图片描述
在前面介绍c++基础知识的时候,就已经介绍了STL模板库,但是很多人并不知道他的原理,而要想弄懂这个问题就要知道容器的接口是什么。
我们所使用的STL模板斗都是将一些代码封装为一个容器,从而使用它,正是由于这样才使得我们的工作减轻很多负担。要想正确的使用模板,了解他们所对应的函数原型显得很重要。下面我们以vector模板为例,介绍他的原函数:
在这里插入图片描述

顺序容器和关联容器

顺序容器将一组具有相同类型的元素一样额的线性形式组织起来。顺序容器分为vector,list,deque三种类型。他们在某些方面很相似。前面已经介绍过了,就不过多介绍。
关联容器具有根据一组索引来快速提取元素的能力,其中元素可以通过键值(key)来访问。四种关联容器可以分为两种:set和map
(1).set是一种集合,其中可以包含0个或多个不重复的以及不排序的元素,这些元素被称为键值。
例如,set集合s
{-2,34,56}
中包含三个键值,不能通过下标来访问集合;
(2).map是一种映像,其中包含0个或多个不排序的元素对,一个元素是不重复的键值,另一个是与键相关联的值。
例如,map集合m
{(first,4),(second,-99),(third,50)}
中包含3对元素。每对元素都由一个键值和相关联的值构成。
map和multimap容器的元素是按照关键字顺序排列的,因此提供按关键字快速查找元素。成员函数find(),count(),lower_bound(),upper_bound()基于元素键值的查找和计数。

#include
#include
#include
using namespace std;
int main()
{
	int i;
	vector<int>nums;        //整型向量,长度为0;
	nums.insert(nums.begin(), -99);    //在第一处插入数据-99