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开发中,在声明变量、函数、类时,c都会要求使用指定的类型。在实际项目过程中,会发现很多代码除了类型不同之外,其他代码看起来都是相同的,为了实现这些相同功能,我们可能会进行如下设计…...
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,您可以毫不费力地在计算机的显示器上录制专业的活动视频。除了录制视频外,Camtasia还允许您从外部源将高清视频导入到录制中。Camtasia的独特之处在于它可以创建包含可单击链接的交互式视频,以生成适用于教室或工作场所的动态视…...
Fabric磁盘扩容后数据迁移
线上环境原来的磁盘比较小,随着业务数据的增多,磁盘需要扩容,因此需要把原来docker数据转移至新的数据盘。 数据迁移 操作系统: centOS 7 docker默认的数据目录为/var/lib/docker 创建一个新的目录/opt/dockerdata&…...
大厂光环下的功能测试,出去面试自动化一问三不知
在一家公司待久了技术能力反而变弱了,原来的许多知识都会慢慢遗忘,这种情况并不少见。一个京东员工发帖吐槽:感觉在大厂快待废了,出去面试问自己接口环境搭建、pytest测试框架,自己做点工太久都忘记了。平时用的时候搜…...
SATA SSD需要NCQ开启吗?
一、故事开篇最近有同学在咨询,SATA SSD是否需要NCQ功能?借此机会,今天我们来聊聊这个比较古老的话题,关于SATA协议的NCQ的故事。首先我们先回顾下SATA与NCQ的历史:2003年,SATA协议1.0问世,传输…...
知识图谱业务落地技术推荐之图神经网络算法库图计算框架汇总
1.PyTorch Geometric: https://pytorch-geometric.readthedocs.io/en/latest/notes/introduction.html PyG是一个基于PyTorch的用于处理不规则数据(比如图)的库,或者说是一个用于在图等数据上快速实现表征学习的框架。它的运行速度很快,训练模型速度可以达到DGL(Deep Gra…...
==与equals()的区别
与equals()的区别 对于 比较的是值是否相等如果作用于基本数据类型的变量,则直接比较其存储的 “值”是否相等;如果作用于引用类型的变量,则比较的是所指向的对象的地址 对于equals方法 equals方法不能作用于基本数据类型的变量ÿ…...
【人工智能】对贝叶斯网络进行吉布斯采样
问题 现要求通过吉布斯采样方法,利用该网络进行概率推理(计算 P(RT|SF, WT)、P2(CF|WT)的概率值)。 原理 吉布斯采样的核心思想为一维一维地进行采样,采某一个维度的时候固定其他的维度,在本次实验中,假…...
Java 面向对象基础
文章目录一、类和对象1. 类的定义2. 对象的使用二、对象内存图三、成员变量和局部变量四、封装1. private 关键字2. this 关键字五、构造方法六、标准类制作一、类和对象 在此之前,我们先了解两个概念,对象和类。 万物皆对象,客观存在的事物…...
RocketMQ源码(21)—ConsumeMessageConcurrentlyService并发消费消息源码
基于RocketMQ release-4.9.3,深入的介绍了ConsumeMessageConcurrentlyService并发消费消息源码。 此前我们学习了consumer消息的拉取流程源码: RocketMQ源码(18)—DefaultMQPushConsumer消费者发起拉取消息请求源码RocketMQ源码(19)—Broker处理Default…...
基于 STM32+FPGA 的多轴运动控制器的设计
运动控制器是数控机床、高端机器人等自动化设备控制系统的核心。为保证控制器的实用性、实时性和稳定 性,提出一种以 STM32 为主控制器、FPGA 为辅助控制器的多轴运动控制器设计方案。给出了运动控制器的硬件电路设计, 将 S 形加减速算法融入运动控制器&…...
《爆肝整理》保姆级系列教程python接口自动化(十三)--cookie绕过验证码登录(详解
python接口自动化(十三)--cookie绕过验证码登录(详解 简介 有些登录的接口会有验证码:短信验证码,图形验证码等,这种登录的话验证码参数可以从后台获取的(或者查数据库最直接)。获取…...
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: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),是一个 Python 文件,以 .py 结尾,包含了 Python 对象定义和Python语句。 模块让你能够有逻辑地组织你的 Python 代码段。 把相关的代码分配到一个模块里能让你的代码更好用,更易懂。 模块能定…...
SAP ERP系统SD模块常用增强之一:VA01/VA02创建或修改SO的输入检查
在SAP/ERP项目的实施中销售管理模块(SD)的创建和修改销售订单必定会有输入字段校验检查的需求,来防止业务人员录入错误或少录入数据,SAP公司也考虑到这一点,所以这方面的配置功能也非常强大,通常情况下不需…...
深度学习知识补充
候选位置(proposal) RCNN 什么时ROI? 在图像处理领域,感兴趣区域(region of interest , ROI) 是从图像中选择的一个图像区域,这个区域是你的图像分析所关注的重点。圈定该区域以便进行进一步处理。使用ROI圈定你想读的目标&…...
Vue笔记(1)——数据代理与绑定
一、初始Vue 1.想让Vue工作,就必须创建一个Vue实例,且要传入一个配置对象; 2.root容器里的代码依然符合html规范,只不过混入了一些特殊的Vue语法; 3.root容器里的代码被称为【Vue模板】; 4.Vue实例和容器是…...
业务系统对接大模型的基础方案:架构设计与关键步骤
业务系统对接大模型:架构设计与关键步骤 在当今数字化转型的浪潮中,大语言模型(LLM)已成为企业提升业务效率和创新能力的关键技术之一。将大模型集成到业务系统中,不仅可以优化用户体验,还能为业务决策提供…...
前端倒计时误差!
提示:记录工作中遇到的需求及解决办法 文章目录 前言一、误差从何而来?二、五大解决方案1. 动态校准法(基础版)2. Web Worker 计时3. 服务器时间同步4. Performance API 高精度计时5. 页面可见性API优化三、生产环境最佳实践四、终极解决方案架构前言 前几天听说公司某个项…...
【大模型RAG】Docker 一键部署 Milvus 完整攻略
本文概要 Milvus 2.5 Stand-alone 版可通过 Docker 在几分钟内完成安装;只需暴露 19530(gRPC)与 9091(HTTP/WebUI)两个端口,即可让本地电脑通过 PyMilvus 或浏览器访问远程 Linux 服务器上的 Milvus。下面…...
HBuilderX安装(uni-app和小程序开发)
下载HBuilderX 访问官方网站:https://www.dcloud.io/hbuilderx.html 根据您的操作系统选择合适版本: Windows版(推荐下载标准版) Windows系统安装步骤 运行安装程序: 双击下载的.exe安装文件 如果出现安全提示&…...
图表类系列各种样式PPT模版分享
图标图表系列PPT模版,柱状图PPT模版,线状图PPT模版,折线图PPT模版,饼状图PPT模版,雷达图PPT模版,树状图PPT模版 图表类系列各种样式PPT模版分享:图表系列PPT模板https://pan.quark.cn/s/20d40aa…...
代理篇12|深入理解 Vite中的Proxy接口代理配置
在前端开发中,常常会遇到 跨域请求接口 的情况。为了解决这个问题,Vite 和 Webpack 都提供了 proxy 代理功能,用于将本地开发请求转发到后端服务器。 什么是代理(proxy)? 代理是在开发过程中,前端项目通过开发服务器,将指定的请求“转发”到真实的后端服务器,从而绕…...
【Linux系统】Linux环境变量:系统配置的隐形指挥官
。# Linux系列 文章目录 前言一、环境变量的概念二、常见的环境变量三、环境变量特点及其相关指令3.1 环境变量的全局性3.2、环境变量的生命周期 四、环境变量的组织方式五、C语言对环境变量的操作5.1 设置环境变量:setenv5.2 删除环境变量:unsetenv5.3 遍历所有环境…...
五子棋测试用例
一.项目背景 1.1 项目简介 传统棋类文化的推广 五子棋是一种古老的棋类游戏,有着深厚的文化底蕴。通过将五子棋制作成网页游戏,可以让更多的人了解和接触到这一传统棋类文化。无论是国内还是国外的玩家,都可以通过网页五子棋感受到东方棋类…...
【UE5 C++】通过文件对话框获取选择文件的路径
目录 效果 步骤 源码 效果 步骤 1. 在“xxx.Build.cs”中添加需要使用的模块 ,这里主要使用“DesktopPlatform”模块 2. 添加后闭UE编辑器,右键点击 .uproject 文件,选择 "Generate Visual Studio project files",重…...
java高级——高阶函数、如何定义一个函数式接口类似stream流的filter
java高级——高阶函数、stream流 前情提要文章介绍一、函数伊始1.1 合格的函数1.2 有形的函数2. 函数对象2.1 函数对象——行为参数化2.2 函数对象——延迟执行 二、 函数编程语法1. 函数对象表现形式1.1 Lambda表达式1.2 方法引用(Math::max) 2 函数接口…...
