当前位置: 首页 > news >正文

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;		//OKstd::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++编译器是支持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{};        //errortemplate<char const * name> class MyClass{};        //error
char const* s = "hi";
MyClass<s> a;    //error,指向内部链接对象extern char const s[] = "hi";
MyClass<s> a;    //OK,指向外部链接对象

四、编译及附录代码

         编译及运行输出

        Makefile:

CX	=	g++GCC_CMD_DEF := 1
BIN 		:= .
TARGET      := test.exe
#FLAGS		:= -staticInclude		:= .
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;		//OKstd::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;			//OKstd::cout << max<char>('a','b') << std::endl;	//OKstd::cout << max('a','b','c') << std::endl;		//OK//QueueData<int> data1;	//OK//QueueData data2;		//errorQueueData<std::string> data3;	//OKdata3.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>
#endiftypedef 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 _WIN32HANDLE m_mutex;
#elsemutable 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 _WIN32m_mutex = ::CreateMutex(NULL, FALSE, NULL);
#elsepthread_mutex_init(&m_mutex, NULL);
#endif
}PYMutex::~PYMutex()
{
#ifdef _WIN32::CloseHandle(m_mutex);
#elsepthread_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
#elsepthread_mutex_lock(&m_mutex);
#endif
}bool PYMutex::TryLock() const
{
#ifdef _WIN32DWORD 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; 
#elsereturn (0==pthread_mutex_trylock(&m_mutex))?true:false;
#endif	
};void PYMutex::Unlock() const
{
#ifdef _WIN32::ReleaseMutex(m_mutex);
#elsepthread_mutex_unlock(&m_mutex);
#endif
}

相关文章:

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

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

mulesoft MCIA 破釜沉舟备考 2023.02.13.04

mulesoft MCIA 破釜沉舟备考 2023.02.13.03 1. An integration Mule application consumes and processes a list of rows from a CSV file.2. One of the backend systems involved by the API implementation enforces rate limits on the number of request a particle clie…...

Camtasia2023最新版本新功能及快捷键教程

使用Camtasia&#xff0c;您可以毫不费力地在计算机的显示器上录制专业的活动视频。除了录制视频外&#xff0c;Camtasia还允许您从外部源将高清视频导入到录制中。Camtasia的独特之处在于它可以创建包含可单击链接的交互式视频&#xff0c;以生成适用于教室或工作场所的动态视…...

Fabric磁盘扩容后数据迁移

线上环境原来的磁盘比较小&#xff0c;随着业务数据的增多&#xff0c;磁盘需要扩容&#xff0c;因此需要把原来docker数据转移至新的数据盘。 数据迁移 操作系统&#xff1a; centOS 7   docker默认的数据目录为/var/lib/docker   创建一个新的目录/opt/dockerdata&…...

大厂光环下的功能测试,出去面试自动化一问三不知

在一家公司待久了技术能力反而变弱了&#xff0c;原来的许多知识都会慢慢遗忘&#xff0c;这种情况并不少见。一个京东员工发帖吐槽&#xff1a;感觉在大厂快待废了&#xff0c;出去面试问自己接口环境搭建、pytest测试框架&#xff0c;自己做点工太久都忘记了。平时用的时候搜…...

SATA SSD需要NCQ开启吗?

一、故事开篇最近有同学在咨询&#xff0c;SATA SSD是否需要NCQ功能&#xff1f;借此机会&#xff0c;今天我们来聊聊这个比较古老的话题&#xff0c;关于SATA协议的NCQ的故事。首先我们先回顾下SATA与NCQ的历史&#xff1a;2003年&#xff0c;SATA协议1.0问世&#xff0c;传输…...

知识图谱业务落地技术推荐之图神经网络算法库图计算框架汇总

1.PyTorch Geometric: https://pytorch-geometric.readthedocs.io/en/latest/notes/introduction.html PyG是一个基于PyTorch的用于处理不规则数据(比如图)的库,或者说是一个用于在图等数据上快速实现表征学习的框架。它的运行速度很快,训练模型速度可以达到DGL(Deep Gra…...

==与equals()的区别

与equals()的区别 对于 比较的是值是否相等如果作用于基本数据类型的变量&#xff0c;则直接比较其存储的 “值”是否相等&#xff1b;如果作用于引用类型的变量&#xff0c;则比较的是所指向的对象的地址 对于equals方法 equals方法不能作用于基本数据类型的变量&#xff…...

【人工智能】对贝叶斯网络进行吉布斯采样

问题 现要求通过吉布斯采样方法&#xff0c;利用该网络进行概率推理&#xff08;计算 P(RT|SF, WT)、P2(CF|WT)的概率值&#xff09;。 原理 吉布斯采样的核心思想为一维一维地进行采样&#xff0c;采某一个维度的时候固定其他的维度&#xff0c;在本次实验中&#xff0c;假…...

Java 面向对象基础

文章目录一、类和对象1. 类的定义2. 对象的使用二、对象内存图三、成员变量和局部变量四、封装1. private 关键字2. this 关键字五、构造方法六、标准类制作一、类和对象 在此之前&#xff0c;我们先了解两个概念&#xff0c;对象和类。 万物皆对象&#xff0c;客观存在的事物…...

RocketMQ源码(21)—ConsumeMessageConcurrentlyService并发消费消息源码

基于RocketMQ release-4.9.3&#xff0c;深入的介绍了ConsumeMessageConcurrentlyService并发消费消息源码。 此前我们学习了consumer消息的拉取流程源码&#xff1a; RocketMQ源码(18)—DefaultMQPushConsumer消费者发起拉取消息请求源码RocketMQ源码(19)—Broker处理Default…...

基于 STM32+FPGA 的多轴运动控制器的设计

运动控制器是数控机床、高端机器人等自动化设备控制系统的核心。为保证控制器的实用性、实时性和稳定 性&#xff0c;提出一种以 STM32 为主控制器、FPGA 为辅助控制器的多轴运动控制器设计方案。给出了运动控制器的硬件电路设计&#xff0c; 将 S 形加减速算法融入运动控制器&…...

《爆肝整理》保姆级系列教程python接口自动化(十三)--cookie绕过验证码登录(详解

python接口自动化&#xff08;十三&#xff09;--cookie绕过验证码登录&#xff08;详解 简介 有些登录的接口会有验证码&#xff1a;短信验证码&#xff0c;图形验证码等&#xff0c;这种登录的话验证码参数可以从后台获取的&#xff08;或者查数据库最直接&#xff09;。获取…...

soapui + groovy 接口自动化测试

1.操作excel的groovy脚本 package pubimport jxl.* import jxl.write.Label import jxl.write.WritableWorkbookclass ExcelOperation {def xlsFiledef workbookdef writableWorkbookdef ExcelOperation(){}//设置xlsFile文件路径def ExcelOperation(xlsFile){this.xlsFile x…...

Linux内存管理(三十五):内存规整简介

源码基于:Linux5.4 0. 前言 伙伴系统以页面为单位来管理内存,内存碎片也是基于页面的,即由大量离散且不连续的页面组成的。从内核角度来看,出现内存碎片不是好事情,有些情况下物理设备需要大段的连续的物理内存,如果内核无法满足,则会发生内核错误。内存规整就是为了解…...

Java连接Redis

Jedis是Redis官方推荐的Java连接开发工具。api&#xff1a;https://tool.oschina.net/apidocs/apidoc?apijedis-2.1.0一、 导入包<!-- https://mvnrepository.com/artifact/redis.clients/jedis --> <dependency><groupId>redis.clients</groupId><…...

Python语言零基础入门教程(十六)

Python 模块 Python 模块(Module)&#xff0c;是一个 Python 文件&#xff0c;以 .py 结尾&#xff0c;包含了 Python 对象定义和Python语句。 模块让你能够有逻辑地组织你的 Python 代码段。 把相关的代码分配到一个模块里能让你的代码更好用&#xff0c;更易懂。 模块能定…...

SAP ERP系统SD模块常用增强之一:VA01/VA02创建或修改SO的输入检查

在SAP/ERP项目的实施中销售管理模块&#xff08;SD&#xff09;的创建和修改销售订单必定会有输入字段校验检查的需求&#xff0c;来防止业务人员录入错误或少录入数据&#xff0c;SAP公司也考虑到这一点&#xff0c;所以这方面的配置功能也非常强大&#xff0c;通常情况下不需…...

深度学习知识补充

候选位置(proposal) RCNN 什么时ROI&#xff1f; 在图像处理领域&#xff0c;感兴趣区域(region of interest &#xff0c; ROI) 是从图像中选择的一个图像区域&#xff0c;这个区域是你的图像分析所关注的重点。圈定该区域以便进行进一步处理。使用ROI圈定你想读的目标&…...

Vue笔记(1)——数据代理与绑定

一、初始Vue 1.想让Vue工作&#xff0c;就必须创建一个Vue实例&#xff0c;且要传入一个配置对象&#xff1b; 2.root容器里的代码依然符合html规范&#xff0c;只不过混入了一些特殊的Vue语法&#xff1b; 3.root容器里的代码被称为【Vue模板】&#xff1b; 4.Vue实例和容器是…...

LeetCode题目笔记——2563. 统计公平数对的数目

文章目录题目描述题目链接题目难度——中等方法一&#xff1a;排序双指针代码/Python代码/C方法二代码/Python总结题目描述 这是前天周赛的第二题。 统计公平数对的数目 - 给你一个下标从 0 开始、长度为 n 的整数数组 nums &#xff0c;和两个整数 lower 和 upper &#xff0c…...

【MySQL Shell】8.9.5 将集群重新加入到 InnoDB ClusterSet

如果 InnoDB 集群是 InnoDB ClusterSet 部署的一部分&#xff0c;MySQL Shell 会在重新启动后立即自动将其恢复到拓扑中的角色&#xff0c;前提是其运行正常且未被标记为无效。但是&#xff0c;如果集群被标记为无效或其 ClusterSet 复制通道已停止&#xff0c;则必须使用 clus…...

元素水平垂直居中的方法有哪些?如果元素不定宽高呢?

实现元素水平垂直居中的方式&#xff1a; 利用定位margin:auto利用定位margin:负值利用定位transformtable布局flex布局grid布局 1-利用定位margin:auto <style>.father{width:500px;height:300px;border:1px solid #0a3b98;position: relative;}.son{width:100px;heig…...

访问学者在新加坡访学生活日常花销大吗?

新加坡地理位置优越&#xff0c;社会发达&#xff0c;教学质量好&#xff0c;吸引不少国内学生前往新加坡留学、访学。那么&#xff0c;去新加坡访学&#xff0c;访问学者花销需要多少钱呢&#xff1f;下面和51访学网小编一起来了解一下吧。 一、饮食 新加坡的饮食从很亲民的…...

XCP实战系列介绍11-几个常用的XCP命令解析

本文框架 1.概述2. 常用命令解析2.1 CONNECT连接(0xFF)2.2 SHORT_UPLOAD 命令(0xF4)2.2 SET_MTA (0xF6)2.3 MOVE命令(0x19)2.4 GET_CAL_PAGE(0xEA)2.5 SET_CAL_PAGE(0xEB)2.6 DOWNLOAD(0xF0)1.概述 在文章《看了就会的XCP协议介绍》中详细介绍了XCP的协议,在《XCP实战系列介绍…...

全志V853芯片 如何在Tina V85x平台切换sensor?

目的 V85x某方案目前默认Sensor是GC2053。实际使用时若需要用到GC4663&#xff08;比如wdr功能&#xff09;和SC530AI&#xff08;支持500W&#xff09;&#xff0c;可按如下步骤完成切换。 步骤 下面以GC4663为例&#xff0c;SC530AI按相应方式适配。 Step1 检查Sensor驱动…...

2023全网最火的接口自动化测试,一看就会

目录 接口自动化测试用例设计Excel接口测试用例访问MySQL接口测试用例访问PyTest测试框架接口自动化测试必备技能-HTTP协议request库实现接口请求 引言 与UI相比&#xff0c;接口一旦研发完成&#xff0c;通常变更或重构的频率和幅度相对较小。因此做接口自动化的性价比更高&…...

华为OD机试真题JAVA实现【最小传递延迟】真题+解题思路+代码(20222023)

🔥系列专栏 华为OD机试(JAVA)真题目录汇总华为OD机试(Python)真题目录汇总华为OD机试(C++)真题目录汇总华为OD机试(JavaScript)真题目录汇总文章目录 🔥系列专栏题目输入输出示例一输入输出说明解题思路核心知识点Code运行结果版权说...

Transformer

Transformer由4部分组成&#xff0c;分别是&#xff1a;输入模块、编码模块、解码模块、输出模块整体架构图&#xff1a;一、输入模块结构 &#xff08;1&#xff09;源文本嵌入层及其位置编码器&#xff08;2&#xff09;目标文本嵌入层及其位置编码器二、编码器模块结构由N个…...

并发包工具之 批量处理任务 CompletionService(异步)、CompletableFuture(回调)

文章目录一、处理异步任务并获取返回值——CompletionService二、线程池三、Callable 与 Future四、通过回调方式处理可组合编排任务——CompletableFuture一、处理异步任务并获取返回值——CompletionService 特点描述&#xff1a; 对于比较复杂的计算&#xff0c;把…...

WordPress自动采集豆瓣评分/seo技巧是什么

嗯…无法重现&#xff1a;在下面的片段中,丢失的内容总是在actionPerfomed之前得到通知,独立于我是否单击按钮或使用助记符&#xff1a;final JTextField field new JTextField("some text to change");FocusAdapter focus new FocusAdapter() {Overridepublic voi…...

网站系统排名/百度推广和优化哪个好

/**//* 标题&#xff1a;<<系统设计师>>应试编程实例-[分治法程序设计]作者&#xff1a;成晓旭时间&#xff1a;2002年09月18日(21:43:00-22:03:00)实现“快速排序算法”问题的分而治之算法函数*/#include "stdio.h"#include "stdlib.h&qu…...

深圳市深圳市住房和建设局网站首页/移动网站如何优化排名

前段时间用ios做了个xml的解析&#xff0c;文件解析还算顺利吧。在ios上的解析步骤&#xff1a;1.使用第三方库2.发动请求 3.解析xml 4.显示数据随后做了一个android上的解析模块&#xff0c;流程如下&#xff1a;1.发送一个http请求&#xff08;java中有内置的…...

网站建设网页设计做网站/软文代写价格

.NET Framework 4.8 发布了。新版包含在Windows 10 May 2019更新中&#xff0c;.NET Framework 4.8也可在Windows 7和Windows Server 2008 R2 上使用。 .NET Framework 4.8包含更新的工具集以及几个方面的改进&#xff1a; [运行时] JIT和NGEN改进 [BCL] 更新了ZLib [BCL] 降…...

网站字体规范/网络培训研修总结

ERP项目的成败关系企业未来的发展。ERP选型关系项目的成败&#xff0c;但很多由于企业对ERP项目了解甚少&#xff0c;在选型方面&#xff0c;做的不是很好。巅峰网ERP实施顾问&#xff0c;结合自己多年的工作经验&#xff0c;总结出企业在选型过程中几种常犯错误&#xff0c;希…...

国外访问国内网站速度/推广平台的方法

回顾一下我们讲述了用户管理、权限管理以及还介绍了两个命令&#xff0c;一个是install命令&#xff0c;另一个是mktemp&#xff0c;在这里我们并不多做介绍。 对于用户管理来说&#xff0c;是对权限的资源进行其隔离分配。 而对于权限来说&#xff0c;是对该系统的登录用户分…...