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实例和容器是…...

使用VSCode开发Django指南
使用VSCode开发Django指南 一、概述 Django 是一个高级 Python 框架,专为快速、安全和可扩展的 Web 开发而设计。Django 包含对 URL 路由、页面模板和数据处理的丰富支持。 本文将创建一个简单的 Django 应用,其中包含三个使用通用基本模板的页面。在此…...
在鸿蒙HarmonyOS 5中实现抖音风格的点赞功能
下面我将详细介绍如何使用HarmonyOS SDK在HarmonyOS 5中实现类似抖音的点赞功能,包括动画效果、数据同步和交互优化。 1. 基础点赞功能实现 1.1 创建数据模型 // VideoModel.ets export class VideoModel {id: string "";title: string ""…...
FFmpeg 低延迟同屏方案
引言 在实时互动需求激增的当下,无论是在线教育中的师生同屏演示、远程办公的屏幕共享协作,还是游戏直播的画面实时传输,低延迟同屏已成为保障用户体验的核心指标。FFmpeg 作为一款功能强大的多媒体框架,凭借其灵活的编解码、数据…...
uni-app学习笔记二十二---使用vite.config.js全局导入常用依赖
在前面的练习中,每个页面需要使用ref,onShow等生命周期钩子函数时都需要像下面这样导入 import {onMounted, ref} from "vue" 如果不想每个页面都导入,需要使用node.js命令npm安装unplugin-auto-import npm install unplugin-au…...
将对透视变换后的图像使用Otsu进行阈值化,来分离黑色和白色像素。这句话中的Otsu是什么意思?
Otsu 是一种自动阈值化方法,用于将图像分割为前景和背景。它通过最小化图像的类内方差或等价地最大化类间方差来选择最佳阈值。这种方法特别适用于图像的二值化处理,能够自动确定一个阈值,将图像中的像素分为黑色和白色两类。 Otsu 方法的原…...
python如何将word的doc另存为docx
将 DOCX 文件另存为 DOCX 格式(Python 实现) 在 Python 中,你可以使用 python-docx 库来操作 Word 文档。不过需要注意的是,.doc 是旧的 Word 格式,而 .docx 是新的基于 XML 的格式。python-docx 只能处理 .docx 格式…...

如何在网页里填写 PDF 表格?
有时候,你可能希望用户能在你的网站上填写 PDF 表单。然而,这件事并不简单,因为 PDF 并不是一种原生的网页格式。虽然浏览器可以显示 PDF 文件,但原生并不支持编辑或填写它们。更糟的是,如果你想收集表单数据ÿ…...
Python 包管理器 uv 介绍
Python 包管理器 uv 全面介绍 uv 是由 Astral(热门工具 Ruff 的开发者)推出的下一代高性能 Python 包管理器和构建工具,用 Rust 编写。它旨在解决传统工具(如 pip、virtualenv、pip-tools)的性能瓶颈,同时…...
AGain DB和倍数增益的关系
我在设置一款索尼CMOS芯片时,Again增益0db变化为6DB,画面的变化只有2倍DN的增益,比如10变为20。 这与dB和线性增益的关系以及传感器处理流程有关。以下是具体原因分析: 1. dB与线性增益的换算关系 6dB对应的理论线性增益应为&…...
PAN/FPN
import torch import torch.nn as nn import torch.nn.functional as F import mathclass LowResQueryHighResKVAttention(nn.Module):"""方案 1: 低分辨率特征 (Query) 查询高分辨率特征 (Key, Value).输出分辨率与低分辨率输入相同。"""def __…...