天天看点

c/c++开发,无可避免的模板编程实践(篇一)

一、c++模板

        c++开发中,在声明变量、函数、类时,c++都会要求使用指定的类型。在实际项目过程中,会发现很多代码除了类型不同之外,其他代码看起来都是相同的,为了实现这些相同功能,我们可能会进行如下设计:

int max(const int &v1, const int &v2){
    return (v1>v2)?v1:v2;
};
double max(const double &v1, const double &v2){
    return (v1>v2)?v1:v2;
};
string max(const string &v1, const string &v2){
    return (v1>v2)?v1:v2;
};
...
           

        这明明针对相同行为的不同类型比较而已,却一次又一次去重复实现它,做了如此多重复的工作,甚至还可能一不留神就引入更多错误,开始一轮痛苦的调整之旅。而借助C++的模板方法,就可以更好地优化此类代码,更好地利用c++类型检查的特点,更好格式的源代码。

        目前模板的应用在c++中非常广泛,如,在c++标准库中,几乎所有代码都是模板代码,很多第三方c++库也或多或少都会使用模板。模板编程是c++开发中无可避免的一道坎。

        模板是为多种类型而编写的函数和类,这些类型都没有指定,等在使用模板时,再把类型作为一个(显式或隐式的)实参传递给模板,完成具体场景化的应用。

二、函数模板

       2.1 函数模板定义

         函数模板提供类一种函数行为,其可以用多种不同的类型进行调用,一个函数模板就代表一个函数家族。前面提到比较两个数据的大小并返回较大值,通过模板可以简化:

//template <class T>	//class 和 typename 没有区别
template <typename T>
T max(const T &v1, const T &v2)
{
	return (v1>v2)?v1:v2;
};
           

        模板定义以关键字 template 开始,后接模板形参表,模板形参表是用尖括号括住的一个或多个模板形参的列表,形参之间以逗号分隔,模板形参表不能为空。这个模板定义指定一个“返回两个值中最大值”的家族函数,参数类型还没确定,采用模板参数T来替代,参数列表是typename T。注意,模板形参作用域和非模板形参的作用域是类似的,这里就不展开阐述了。

        在这里class T和 typename T是等价的,鉴于历史原因,可能会使用class取代typename,来定义类型参数。在c++语言发展演化过程中,关键字typename 的出现相对较晚一些,在它之前都是关键字class作为模板的类型参数引入的,c++也保留了下来。若要支持一些旧版本编译工具、库等就采用class,否则建议使用关键字typename。但需要注意struct关键字是不行的,不要因为class可行就产生误导了。

        像函数形参一样,程序员为模板形参选择的名字没有本质含义。将 max的模板类型形参命名为 T,但也可以将它命名为任意名字:

template <typename this_is_long_name>
this_is_long_name max(const this_is_long_name &v1, const this_is_long_name &v2)
{
	return (v1>v2)?v1:v2;
};
           

        2.2 模板调用

        在使用模板时,编译器会根据传入实参进行检测,编译器会推断哪个(或哪些)模板实参绑定到模板形参。一旦编译器确定了实际的模板实参,就称它实例化了函数模板的一个实例。实质上,编译器将确定用什么类型代替每个类型形参,以及用什么值代替每个非类型形参。推导出实际模板实参后,编译器使用实参代替相应的模板形参产生编译该版本的函数。

max<int>(14,16);	
//编译器实质类似于
int max_int(const int &v1, const int &v2)
{
	return (v1>v2)?v1:v2;
};
 
max_int(14,16);	
           

        上述代码展示,使用了int作为模板参数T的函数模板,就会用具体类型替代模板参数,这个过程就叫实例化,它会产生一个模板实例。只要使用了函数模板,编译器就会自动触发这样的一个实例化过程,因此开发者并不需要额外地请求模板的实例化。

        在使用函数模板时,通过具体参数类型传递实参,编译器就会明确参数类型,其不允许自动类型转换,每个模板参数都必须正确地匹配。

std::cout << max(14,16) << std::endl;		//OK
	std::cout << max<int>(14,16) << std::endl;	//OK,建议
	std::cout << max<std::string>(("nihao"),("hi")) << std::endl;	//显式指定
	//std::cout << max("nihao","hi") << std::endl;					//error,编译器无法明确
	//std::cout << max(14,16.5) << std::endl;					    //error,类型不匹配
	std::cout << max<float>(14,16.5) << std::endl;                  //OK,显式指定
    std::cout << max(static_cast<double>(14),16.5) << std::endl;     //OK,强制类型转换
           

        2.3 非类型模板形参

        模板形参不必都是类型,函数模板也可以使用的非类型形参。

template <typename T, int LEN>
void print(const T (&val)[LEN])
{
	for(int i=0; i<LEN; i++)
    {
		std::cout << val[i] << std::endl;
    }
};

//
std::string str[2] = {"hello","world"};
print(str);				//OK 
print<std::string>(str);//OK
           

        2.4 模板声明和定义源码分离

        模板的声明和定义一般都建议放置同一头文件内,将声明和定义分离到头文件及源文件内,c++编译器会接受,但连接器会出错。

/*test_template.h*/
#ifndef _TEST_TEMPLATE_H_
#define _TEST_TEMPLATE_H_

//
template <typename T> 
T min(const T &v1, const T &v2);

#endif //_TEST_TEMPLATE_H_


/*test_template.cpp*/
#include "test_template.h"

template <typename T>
T min(const T &v1, const T &v2)
{
	return (v1<v2)?v1:v2;
};

/*test.cpp*/
#include "test_template.h"
int main(int argc, char* argv[])
{
	std::cout << min(14,16) << std::endl;		
	return 0;
};
           

        编译时,连接器会出错,

ccZMcsD7.o:test.cpp:(.text+0x207): undefined reference to `int min<int>(int const&, int const&)'
collect2.exe: error: ld returned 1 exit status
           

       当然您也可以在头文件里包含源文件,等同于模板的声明和定义一般都建议放置同一头文件内。

/*test_template.h*/
#ifndef _TEST_TEMPLATE_H_
#define _TEST_TEMPLATE_H_

//
template <typename T> 
T min(const T &v1, const T &v2);

#include "test_template.cpp"    //仅这里不同了哦-------------------
#endif //_TEST_TEMPLATE_H_
/*---------------------------------------------------*
/*test_template.cpp*/
#include "test_template.h"

template <typename T>
T min(const T &v1, const T &v2)
{
	return (v1<v2)?v1:v2;
};
/*---------------------------------------------------*
/*test.cpp*/
#include "test_template.h"
int main(int argc, char* argv[])
{
	std::cout << min(14,16) << std::endl;		
	return 0;
};
           

         因此建议是模板的声明和定义都放置同一头文件内。

#ifndef _TEST_TEMPLATE_H_
#define _TEST_TEMPLATE_H_

//
template <typename T> 
T min(const T &v1, const T &v2);
{
	return (v1<v2)?v1:v2;
};
#endif //_TEST_TEMPLATE_H_
           

        如果非要进行分离,也是有办法就是较麻烦。另创建一个头文件放置模板函数的定义,为了区别,起名.hpp,然后再创建一个头文件,对模板函数进行显式实例化,就可以避免链接期报错。

/*test_template.h*/
#ifndef _TEST_TEMPLATE_H_
#define _TEST_TEMPLATE_H_
//
template <typename T> 
T min(const T &v1, const T &v2);

#endif //_TEST_TEMPLATE_H_

/*test_template.hpp*/
#ifndef _TEST_TEMPLATE_HPP_
#define _TEST_TEMPLATE_HPP_

#include "test_template.h"

template <typename T>
T min(const T &v1, const T &v2)
{
	return (v1<v2)?v1:v2;
};

#endif //_TEST_TEMPLATE_HPP_

/*test_template.cpp*/
#include "test_template.hpp"
template int min<int>(const int &v1, const int &v2);
template double min<double >(const double &v1, const double &v2);

/*test.cpp*/
#include "test_template.h"
int main(int argc, char* argv[])
{
	std::cout << min(14,16) << std::endl;	//OK	
	return 0;
};
           

        再编译时就可以通过。

c/c++开发,无可避免的模板编程实践(篇一)

         另外如果您手头上的c++编译器是支持export这个关键字特性的话,做模板声明和定义分离编写会更容易。

/*test_template.h*/
#ifndef _TEST_TEMPLATE_H_
#define _TEST_TEMPLATE_H_
//
export template <typename T> 
T min(const T &v1, const T &v2);

#endif //_TEST_TEMPLATE_H_

/*test_template.cpp*/
#include "test_template.h"

export template <typename T>
T min(const T &v1, const T &v2)
{
	return (v1<v2)?v1:v2;
};


/*test.cpp*/
#include "test_template.h"
int main(int argc, char* argv[])
{
	std::cout << min(14,16) << std::endl;	//OK	
	return 0;
};
           

        但是,关于export这个关键字特性,目前市面上支持的C++编译器是较少的,因此采用export时,可能就通过宏定义编译条件了集合上述两种方法才能做到代码在各个编译器平滑支持了。

        2.5 函数模板支持inline

        另外函数模板可以用与非模板函数一样的方式声明为 inline。inline说明符放在模板形参表之后、返回类型之前,不能放在关键字 template 之前。

template <typename T> inline T test(const T &v1){return v1;}; //OK
inline template <typename T> T test(const T &v1){return v1;}; //error
           

        2.6 函数模板重载

        和普通函数一样,函数模板也可以被重载,相同模板函数名称可具有不同函数定义,当使用函数名称进行模板函数调用时,编译器会根据调用语句进行检查推断调用匹配度更高的哪个候选模板函数。

template <typename T>
inline T max(const T &v1, const T &v2)
{
	return (v1>v2)?v1:v2;
};

template <typename T>
T max(const T &v1, const T &v2, const T &v3)
{
	return max(max(v1,v2),v3);
};

//
std::cout << max(14,16,15) << std::endl;		//OK
           

        但需要注意的是,模板函数和普通函数重载时,参数个数不一致是不行的

template <typename T>
inline T max(const T &v1, const T &v2)
{
	return (v1>v2)?v1:v2;
};

int max(const int &v1){	return 0;}    //编译错误,multiple definition of `max'
           

        但如果普通函数看着和模板函数函数名相同,参数格式一致,那就是模板函数的特例化实现。

template <typename T>
inline T max(const T &v1, const T &v2)
{
	return (v1>v2)?v1:v2;
};

inline char max(const char &v1, const char &v2)
{
	return (v1>v2)?v1:v2;
};

std::cout << max('a','c') << std::endl;	//OK,大家认为会调用哪个函数呢,如果第二个函数在前呢
std::cout << max<char>('a','b') << std::endl;	
           

        普通函数(非模板函数)和模板函数同名及参数格式一致时,该模板函数可以被实例化为这个非模板函数,在调用时,编译器会根据重载解析过程调用匹配度更高的函数。在看看下面案例:

template <typename T>
inline T max(const T &v1, const T &v2)
{
	return (v1>v2)?v1:v2;
};

template <typename T>
T max(const T &v1, const T &v2, const T &v3)
{
	return max(max(v1,v2),v3);    //使用模板函数,int的函数声明太迟了
};

inline char max(const char &v1, const char &v2)
{
	return (v1>v2)?v1:v2;
};

std::cout << max('a','b','c') << std::endl;		//OK,这个呢,会调用那个函数(两个参数的max)
           

三、类模板

        3.1 类模板定义

        和函数一样,类也可以被定义为模板,像标准库中的容器就类模板,它被用于管理某种特定类型的元素,在使用它们时,不需要确定容器中元素的类型。下面以本人曾定义过的一个类模板为例来唠叨一下类模板:

/*queuedata.h*/
template <class T>
class QueueData
{
public:
	QueueData(std::string desc = "thread_queue");
	~QueueData();
	//
	/**
	 * 获取队列大小
	 * @return {int } 队列大小
	 */
	int size();
	/**
	 * 判定队列是否为空
	 * @return {bool } 是否为空队列
	 */
	bool isEmpty();
	/**
	 * 获取队列头元素
	 * @param it {T&} 头元素
	 * @return {bool } 是否成功
	 */
	bool getFirst(T &it);
	/**
	 * 删除元素
	 * @return {bool } 是否成功
	 */
	bool removeFirst();
	/**
	 * 获取队列头元素,并从队列终删除
	 * @param it {T&} 头元素
	 * @return {bool } 是否成功
	 */
	bool pop(T &it);
	/**
	 * 从队列头开始逐步获取多个元素,并剔除
	 * @param its {queue<T>&} 获取到的元素集
	 * @param sizel {int} 一次获取多少个
	 * @return {bool } 至少获取一个元素以上则成功
	 */
	bool getList(std::queue<T> &its,unsigned int sizel=5);
	/**
	 * 从队列尾部添加元素
	 * @param it {T} 被添加元素
	 * @return {void } 无返回
	 */
	void add(T it);
	/**
	 * 从队列头部添加元素
	 * @param it {T} 被添加元素
	 * @return {void } 无返回
	 */
	void add_front(T it);
	/**
	 * 清空元素
	 * @return {void }
	 */
	void clear();
private:
	void init();
	QueueData& operator=(const QueueData&) {return this;};
protected:
	std::string queue_desc;
private:
	/点集转发
	
	//协议解析结果缓存
	std::deque<T> datacache_queue;	//队列容器
	PYMutex m_Mutex;				//线程锁,或者如果更彻底采用acl库,采用acl::thread_mutex替代
	//
	static unsigned int QSize;		//队列大小约束,超出是会从队列头剔除旧数据腾出空位在对末添加数据
	//
	int queue_overS;				//队列溢出次数计数
};
           

        类模板也是模板,因此必须以关键字 template 开头,后接模板形参表。QueueData模板接受一个名为 T 的模板类型形参。

        除了模板形参表外,类模板的定义看起来与任意其他类问相似。 类模板可以定义数据成员、函数成员和类型成员,也可以使用访问标号控制对成员的访问,还可以定义构造函数和析构函数等等。在类和类成员的定义中,可以使用模板形参作为类型或值的占位符,在使用类时再提供那些类型或值。

        例如,上述QueueData模板有一个模板类型形参,可以在任何可以使用类型名字的地方使用该形参。在这个模板定义中,用 T作为 add操作的形参类型。QueueData模板是基于标准库中的std::deque<>来实现的,就不需要亲自实现内存管理、拷贝构造、赋值及算法等,把精力聚焦在业务逻辑开发上。

        和函数模板调用对比,使用类模板时,必须为模板形参显式指定实参。编译器使用实参来实例化这个类的特定类型版本,QueueData 不是类型,而 QueueData <int>或 QueueData <string> 是类型。实质上,编译器用用户提供的实际特定类型代替 T,重新编写 QueueData类。

#include "queuedata.h"

QueueData<int> data1;	//OK
//QueueData data2;		//error
QueueData<std::string> data3;	//OK
           

        3.2 类模板的成员变量及成员函数

        类模板的成员函数定义和函数模板分离时定义类似

//queuedata.h
template <class T>
void QueueData<T>::init() 
{
	queue_overS = 0;
};
           

        采用模板参数T定义的成员变量也和普通类成员变量用法一致

template <class T>
class QueueData{
// 省略其他
private:
    std::deque<T> datacache_queue;	//队列容器
};

template <class T>
void QueueData<T>::add(T it) 
{
	m_Mutex.Lock();
	if (datacache_queue.size() > QSize) 
	{
		queue_overS++;
		datacache_queue.pop_front();
	}
	datacache_queue.push_back(it);
	m_Mutex.Unlock();
	if (queue_overS >= 10) 
	{
		//每溢出10次,报告一次
		printf("add item to queue %s at end,but the size of QueueData is up to limmit size: %d.\n"
			, queue_desc.c_str(), QSize);
		queue_overS = 0;
	}
}
           

        对于static静态成员,用法也一致,无非就是带上模板定义而已:

template <class T>
class QueueData{
// 省略其他
private:
    static unsigned int QSize;//队列大小约束,超出是会从队列头剔除旧数据腾出空位在对末添加数据
};

//
template <class T>
unsigned int  QueueData<T>::QSize = 100;
           

        3.3 类模板的特例化

        类模板可以通过指定实参来特例化,和函数模板的重载类似,通过特化类模板,通常是用来可以克服某种特定类型在实例化类模板时所出现的不足。

template <>
class QueueData<std::string>
{
    ...
};
//相应地,类模板特例化时,每个成员函数必须都重新定义为普通函数,模板参数T也被特例化类型取代。
int QueueData<std::string>::size()
{
	int ret = 0;
	m_Mutex.Lock();
	ret = static_cast<int>(datacache_queue.size());
	m_Mutex.Unlock();
	return ret;
}
           

        当类模板有多个模板参数时,也可以进行局部特例化

template <typename T1, typename T2>
class TestData
{
    ...
};
//局部特例化,两个模板参数相同
template <typename T>
class TestData<T,T>
{
    ...
};

//局部特例化,第2模板参数int
template <typename T>
class TestData<T,int>
{
    ...
};
//局部特例化,两个模板参数改为指针
template <typename T1, typename T2>
class TestData<*T1,*T2>
{
    ...
};
           

       3.4 非类型模板参数及缺省值

         对于类模板,还可以为模板参数定义缺省值。

template<typename T, typename VALS= std::vector<T> >
class TestData{
    private:
    VALS els;
};
           

        此外,对于模板,模板参数并不局限于类型,普通值也可以作为模板参数,可以给类模板指定类型及指定缺省值,本专栏在讲述TCP/Socket通信代码优化时就用到过:

#define RDC_SIZE 1024	//服务端从socket读取数据时的缓存大小
#define DATA_SIZE 512	//服务及客户端的数据传输(读取及写入)的缓存大小

template<int SIZE = DATA_SIZE>
struct TCP_Data
{
	TCP_Data() 
		: len(0)
	{
		memset(Buf,0,SIZE);
	};
    ...省略其他...
	unsigned char Buf[SIZE];
	int len;
};

//调用
TCP_Data<> a;          //大小512
TCP_Data<DATA_SIZE> a; //大小512
           

        在这个例子里,我们使用元素数目固定的数组来实现,其优点就是避免内存开销,让用户亲自制定数组的大小。当然这个例子极端了些,再看看下面这个:

template<typename T, int MAXSIZE>
class TCP_Data
{
public:
    TCP_Data();
    void doit();
private:
    int len;
    T els[MAXSIZE];
};
//定义成员函数
template<typename T, int MAXSIZE>
TCP_Data<T,MAXSIZE>::TCP_Data() 
	: len(0)
{
};
//
template<typename T, int MAXSIZE>
void TCP_Data<T,MAXSIZE>::doit()
{
};

//使用时,同时制定模板参数类型和最大容量
TCP_Data<int,10> a;
           

        特别注意的是,非类型模板参数是有限制的,它们常整数(包括枚举值)或者指向外部链接对象的指针。

template<double VAT> double doit(double val){};    //error
template<std::string name> class MyClass{};        //error

template<char const * name> class MyClass{};        //error
char const* s = "hi";
MyClass<s> a;    //error,指向内部链接对象

extern char const s[] = "hi";
MyClass<s> a;    //OK,指向外部链接对象
           

四、编译及附录代码

c/c++开发,无可避免的模板编程实践(篇一)

         编译及运行输出

c/c++开发,无可避免的模板编程实践(篇一)

        Makefile:

CX	=	g++

GCC_CMD_DEF := 1
BIN 		:= .
TARGET      := test.exe
#FLAGS		:= -static

Include		:= .
source		:= test.cpp Mutex.cpp test_template.cpp 
#可以注释掉test_template.cpp 并在test_template.h的26行开启测试另一种伪分离的效果
$(TARGET) :
	$(CX) $(FLAGS) $(source) -I$(Include) -o $(BIN)/$(TARGET)

clean:
	rm  $(BIN)/$(TARGET)
           

        test_template.h

#ifndef _TEST_TEMPLATE_H_
#define _TEST_TEMPLATE_H_

#include <iostream>
//template <class T>	//class 和 typename 没有区别
template <typename T>
inline T max(const T &v1, const T &v2)
{
	return (v1>v2)?v1:v2;
};

template <typename T>
T max(const T &v1, const T &v2, const T &v3)
{
	return max(max(v1,v2),v3);
};

inline char max(const char &v1, const char &v2)
{
	return (v1>v2)?v1:v2;
};

//
template <typename T> 
T min(const T &v1, const T &v2);
//#include "test_template.hpp"	开该项,就不需要test_template.cpp咯

template <typename T, int LEN>
void print(const T (&val)[LEN])
{
	for(int i=0; i<LEN; i++)
	{
		std::cout << val[i] << std::endl;
	}
};

template<typename T, int MAXSIZE>
class TCP_Data
{
public:
	TCP_Data();
	~TCP_Data();
	void add(T const & elem);
    void doit();
private:
	int len;
    T els[MAXSIZE];
	
};
//定义成员函数
template<typename T, int MAXSIZE>
TCP_Data<T,MAXSIZE>::TCP_Data() 
	: len(0)
{
	
};

template<typename T, int MAXSIZE>
TCP_Data<T,MAXSIZE>::~TCP_Data()
{
};

template<typename T, int MAXSIZE>
void TCP_Data<T,MAXSIZE>::add(T const & elem)
{
	if(len>=MAXSIZE)
	{
		//
		return;
	}
	els[len] = elem;
	++len;
};
	
template<typename T, int MAXSIZE>
void TCP_Data<T,MAXSIZE>::doit()
{
	for(int i=0; i<len; i++)
	{
		std::cout << els[i] << std::endl;	//若T是自定义类型呢
	}
};

#endif //_TEST_TEMPLATE_H_
           

        test_template.hpp

#ifndef _TEST_TEMPLATE_HPP_
#define _TEST_TEMPLATE_HPP_

#include "test_template.h"

template <typename T>
T min(const T &v1, const T &v2)
{
	return (v1<v2)?v1:v2;
};

#endif //_TEST_TEMPLATE_HPP_
           

        test_template.cpp

#include "test_template.hpp"

template int min<int>(const int &v1, const int &v2);
template double min<double>(const double &v1, const double &v2);
           

        test.cpp

#include <iostream>
#include "test_template.h"
#include "queuedata.h"

int main(int argc, char* argv[])
{
	//模板调用
	std::cout << max(14,16) << std::endl;		//OK
	std::cout << max<int>(14,16) << std::endl;	//OK,建议
	std::cout << max<std::string>(("nihao"),("hi")) << std::endl;	//显式指定
	//std::cout << max("nihao","hi") << std::endl;					//error,编译器无法明确
	//std::cout << max(14,16.5) << std::endl;					    //error,类型不匹配
	std::cout << max<float>(14,16.5) << std::endl;                  //OK,显式指定
    std::cout << max(static_cast<double>(14),16.5) << std::endl;     //OK,强制类型转换
	//
	std::string str[2] = {"hello","world"};
	print(str);				//OK 
	print<std::string>(str);//OK
	//分离声明及定义
	std::cout << min(14,16) << std::endl;		//OK
	//重载
	std::cout << max(14,16,15) << std::endl;		//OK
	//重载
	std::cout << max('a','c') << std::endl;			//OK
	std::cout << max<char>('a','b') << std::endl;	//OK
	std::cout << max('a','b','c') << std::endl;		//OK
	//
	QueueData<int> data1;	//OK
	//QueueData data2;		//error
	QueueData<std::string> data3;	//OK
	data3.add("hi");
	data3.add_front("hello");
	std::string s_="0";
	data3.getFirst(s_);
	std::cout <<"first: " << s_ << std::endl;			//OK
	//
	TCP_Data<int,2> vals;
	vals.add(7);
	vals.add(17);
	vals.doit();
	return 0;
};
           

        queuedata.h

#if _MSC_VER > 1000
#pragma once
#endif // _MSC_VER > 1000

#ifndef _QUEUE_DATA_H_
#define _QUEUE_DATA_H_

/***********************************************************************
  *Copyright 2020-03-06, pyfree
  *
  *File Name       : queuedata.h
  *File Mark       : 
  *Summary         : 
  *数据队列类,线程安全
  *
  *Current Version : 1.00
  *Author          : pyfree
  *FinishDate      :
  *
  *Replace Version :
  *Author          :
  *FinishDate      :

 ************************************************************************/
#include <queue>
#include <deque>
#include <stdio.h>
#include <string.h>

#include "Mutex.h"

template <class T>
class QueueData
{
public:
	QueueData(std::string desc = "thread_queue");
	~QueueData();
	//
	/**
	 * 获取队列大小
	 * @return {int } 队列大小
	 */
	int size();
	/**
	 * 判定队列是否为空
	 * @return {bool } 是否为空队列
	 */
	bool isEmpty();
	/**
	 * 获取队列头元素
	 * @param it {T&} 头元素
	 * @return {bool } 是否成功
	 */
	bool getFirst(T &it);
	/**
	 * 删除元素
	 * @return {bool } 是否成功
	 */
	bool removeFirst();
	/**
	 * 获取队列头元素,并从队列终删除
	 * @param it {T&} 头元素
	 * @return {bool } 是否成功
	 */
	bool pop(T &it);
	/**
	 * 从队列头开始逐步获取多个元素,并剔除
	 * @param its {queue<T>&} 获取到的元素集
	 * @param sizel {int} 一次获取多少个
	 * @return {bool } 至少获取一个元素以上则成功
	 */
	bool getList(std::queue<T> &its,unsigned int sizel=5);
	/**
	 * 从队列尾部添加元素
	 * @param it {T} 被添加元素
	 * @return {void } 无返回
	 */
	void add(T it);
	/**
	 * 从队列头部添加元素
	 * @param it {T} 被添加元素
	 * @return {void } 无返回
	 */
	void add_front(T it);
	/**
	 * 清空元素
	 * @return {void }
	 */
	void clear();
private:
	void init();
	QueueData& operator=(const QueueData&) {return this;};
protected:
	std::string queue_desc;
private:
	/点集转发
	
	//协议解析结果缓存
	std::deque<T> datacache_queue;	//队列容器
	PYMutex m_Mutex;				//线程锁,或者如果更彻底采用acl库,采用acl::thread_mutex替代
	//
	static unsigned int QSize;		//队列大小约束,超出是会从队列头剔除旧数据腾出空位在对末添加数据
	//
	int queue_overS;				//队列溢出次数计数
};
template <class T>
unsigned int  QueueData<T>::QSize = 100;

template <class T>
QueueData<T>::QueueData(std::string desc)
	: queue_desc(desc)
{
	init();
};

template <class T>
void QueueData<T>::init() 
{
	queue_overS = 0;
};

template <class T>
QueueData<T>::~QueueData()
{

}

//
template <class T>
int QueueData<T>::size()
{
	int ret = 0;
	m_Mutex.Lock();
	ret = static_cast<int>(datacache_queue.size());
	m_Mutex.Unlock();
	return ret;
}

template <class T>
bool QueueData<T>::isEmpty()
{
	bool ret = false;
	m_Mutex.Lock();
	ret = datacache_queue.empty();
	m_Mutex.Unlock();
	return ret;
}

template <class T>
bool QueueData<T>::getFirst(T &it) 
{
	bool ret = false;
	m_Mutex.Lock();
	if (!datacache_queue.empty()) 
	{
		it = datacache_queue.front();
		ret = true;
	}
	m_Mutex.Unlock();
	return ret;
}

template <class T>
bool QueueData<T>::removeFirst() 
{
	bool ret = false;
	m_Mutex.Lock();
	if (!datacache_queue.empty()) 
	{
		datacache_queue.pop_front();
		ret = true;
	}
	m_Mutex.Unlock();
	return ret;
}

template <class T>
bool QueueData<T>::pop(T &it)
{
	bool ret = false;
	m_Mutex.Lock();
	if (!datacache_queue.empty()) 
	{
		it = datacache_queue.front();
		datacache_queue.pop_front();
		ret = true;
	}
	m_Mutex.Unlock();
	return ret;
};

template <class T>
bool QueueData<T>::getList(std::queue<T> &its,unsigned int sizel)
{
	m_Mutex.Lock();
	while (!datacache_queue.empty())
	{
		its.push(datacache_queue.front());
		datacache_queue.pop_front();
		if (its.size() >= sizel)
		{
			break;
		}
	}
	m_Mutex.Unlock();
	return !its.empty();
};

template <class T>
void QueueData<T>::add(T it) 
{
	m_Mutex.Lock();
	if (datacache_queue.size() > QSize) 
	{
		queue_overS++;
		datacache_queue.pop_front();
	}
	datacache_queue.push_back(it);
	m_Mutex.Unlock();
	if (queue_overS >= 10) 
	{
		//每溢出10次,报告一次
		printf("add item to queue %s at end,but the size of QueueData is up to limmit size: %d.\n"
			, queue_desc.c_str(), QSize);
		queue_overS = 0;
	}
}

template <class T>
void QueueData<T>::add_front(T it)
{
	m_Mutex.Lock();
	if (datacache_queue.size() > QSize) 
	{
		queue_overS++;
		datacache_queue.pop_front();
	}
	datacache_queue.push_front(it);
	m_Mutex.Unlock();
	if (queue_overS >= 10) 
	{
		//每溢出10次,报告一次
		printf("add item to queue %s at first,but the size of QueueData is up to limmit size: %d.\n"
			, queue_desc.c_str(), QSize);
		queue_overS = 0;
	}
}

template <class T>
void QueueData<T>::clear()
{
	m_Mutex.Lock();
	datacache_queue.clear();
	m_Mutex.Unlock();
	queue_overS = 0;
}

#endif //_QUEUE_DATA_H_
           

        Mutex.h

#if _MSC_VER > 1000
#pragma once
#endif // _MSC_VER > 1000

#ifndef _PYMUTEX_H_
#define _PYMUTEX_H_

/***********************************************************************
  *Copyright 2020-03-06, pyfree
  *
  *File Name       : Mutex.h
  *File Mark       : 
  *Summary         : 线程锁
  *
  *Current Version : 1.00
  *Author          : pyfree
  *FinishDate      :
  *
  *Replace Version :
  *Author          :
  *FinishDate      :

 ************************************************************************/

#ifdef WIN32
//#include <windows.h>
#else
#include <pthread.h>
#endif

typedef void *HANDLE;

class IMutex
{
public:
	virtual ~IMutex() {}

  /**
	 * 上锁
	 * @return {void} 
	 */
	virtual void Lock() const = 0;
  /**
	 * 尝试上锁
	 * @return {void} 
	 */
	virtual bool TryLock() const = 0;
  /**
	 * 解锁
	 * @return {void} 
	 */
	virtual void Unlock() const = 0;
};

class PYMutex : public IMutex
{
public:
	PYMutex();
	~PYMutex();

	virtual void Lock() const;
	virtual bool TryLock() const;
	virtual void Unlock() const;
private:
#ifdef _WIN32
	HANDLE m_mutex;
#else
	mutable pthread_mutex_t m_mutex;
#endif
};

#endif //_PYMUTEX_H_
           

        Mutex.cpp

#include "Mutex.h"

#ifdef WIN32
#include <windows.h>
#endif
//#include <iostream>
#include <stdio.h>

PYMutex::PYMutex()
{
#ifdef _WIN32
	m_mutex = ::CreateMutex(NULL, FALSE, NULL);
#else
	pthread_mutex_init(&m_mutex, NULL);
#endif
}


PYMutex::~PYMutex()
{
#ifdef _WIN32
	::CloseHandle(m_mutex);
#else
	pthread_mutex_destroy(&m_mutex);
#endif
}


void PYMutex::Lock() const
{
#ifdef _WIN32
	//DWORD d = WaitForSingleObject(m_mutex, INFINITE);
	WaitForSingleObject(m_mutex, INFINITE);
	/// \todo check 'd' for result
#else
	pthread_mutex_lock(&m_mutex);
#endif
}

bool PYMutex::TryLock() const
{
#ifdef _WIN32
    DWORD dwWaitResult = WaitForSingleObject(m_mutex, 0);  
	if (dwWaitResult != WAIT_OBJECT_0 && dwWaitResult != WAIT_TIMEOUT) {
		printf("thread WARNING: bad result from try-locking mutex\n");
	}
    return (dwWaitResult == WAIT_OBJECT_0) ? true : false; 
#else
	return (0==pthread_mutex_trylock(&m_mutex))?true:false;
#endif	
};

void PYMutex::Unlock() const
{
#ifdef _WIN32
	::ReleaseMutex(m_mutex);
#else
	pthread_mutex_unlock(&m_mutex);
#endif
}
           

继续阅读