时间戳一般用来唯一地标识某一刻的时间,通常是指格林威治时间1970年01月01日00时00分00秒(北京时间1970年01月01日08时00分00秒)起至现在的总毫秒数。
muduo网络库对时间戳Timestamp类也进行了封装。
由类图可知,Timestamp类的参数有一个常量kMicroSecondsPerSecond表示每秒所对应的微秒数,成员变量microSecondsSinceEpoch_表示到1970-01-01 00:00:00 UTC的微秒数。成员函数包括swap()交换操作,toString()、toFormattedString()将时间转换为string类型或指定格式,valid()判断Timestamp是否有效,invalid()返回一个无效的Timestamp,now()返回当前时间的Timestamp,secondsSinceEpoch()/microSecondsSinceEpoch()返回到1970-01-01 00:00:00 UTC的秒数/微秒数。
代码中的一些要点如下:
(1)Timestamp类继承自boost::less_than_comparable
只要实现 <,即可自动实现>,<=,>=
(2)使用到了BOOST_STATIC_ASSERT,编译时断言
我们一般使用的assert是运行时断言,用一个小例子来理解BOOST_STATIC_ASSERT:
#include
class Timestamp
{
private:
int64_t microSecondsSinceEpoch_;
};
// 编译时断言通过
// BOOST_STATIC_ASSERT(sizeof(Timestamp) == sizeof(int64_t));
// 编译时断言失败
BOOST_STATIC_ASSERT(sizeof(int) == sizeof(short));
int main(void)
{
return 0;
}
(3)gmtime和gmtime_r函数
用法:
struct tm *gmtime(const time_t *timep);
struct tm *gmtime_r(const time_t *timep, struct tm *result);
gmtime(线程不安全的)是把日期和时间转换为格林威治(GMT)时间的函数。将参数timep 所指的time_t 结构中的信息转换成真实世界所使用的时间日期表示方法,然后将结果由结构tm返回。使用gmtime后要立即处理结果,否则返回的指针指向的内容可能会被覆盖。一个好的方法是使用gmtime_r(线程安全的),gmtime_r()函数功能与此相同,但是它可以将数据存储到用户提供的结构体中,由于使用了用户分配的内存,是不会出错的。
节选muduo源码中的使用示例:
string Timestamp::toFormattedString() const
{
char buf[32] = {0};
time_t seconds = static_cast(microSecondsSinceEpoch_ / kMicroSecondsPerSecond); // 秒数
int microseconds = static_cast<int>(microSecondsSinceEpoch_ % kMicroSecondsPerSecond);
struct tm tm_time; // 微秒数
// 把time_t结构中的信息转换成真实世界所使用的时间日期,存储在tm_time结构中
gmtime_r(&seconds, &tm_time);
// 格式化输出时间戳
snprintf(buf, sizeof(buf), "%4d%02d%02d %02d:%02d:%02d.%06d",
tm_time.tm_year + 1900, tm_time.tm_mon + 1, tm_time.tm_mday,
tm_time.tm_hour, tm_time.tm_min, tm_time.tm_sec,
microseconds);
return buf;
}
(4)函数参数采用值传递
例如:
inline Timestamp addTime(Timestamp timestamp, double seconds)
{
int64_t delta = static_cast(seconds * Timestamp::kMicroSecondsPerSecond);
return Timestamp(timestamp.microSecondsSinceEpoch() + delta);
}
类对象作为参数传递并不一定采用引用传递更高效,这里采用值传递,是因为Timestamp类只包含一个类型为int64_t的数据成员microSecondsSinceEpoch_,所以我们可以把Timestamp对象看作是一个64位(8字节)的整数。参数传递的过程中,会把参数传递到一个8字节的寄存器中而不是传递到堆栈当中(在对应的64位平台),它的效率会更高。
(5)使用PRId64
int64_t用来表示64位整数,在32位系统中是long long int,在64位系统中是long int,所以打印int64_t的格式化方法是:
printf(“%ld”, value); // 64bit OS
printf("%lld", value); // 32bit OS
这种做法是不可移植的。
跨平台的做法:
// C++使用PRID64,需要两步:
// 包含头文件:
// 定义宏:__STDC_FORMAT_MACROS,可以通过编译时加-D__STDC_FORMAT_MACROS,或者在包含文件之前定义这个宏。
#define __STDC_FORMAT_MACROS
#include
#undef __STDC_FORMAT_MACROS
printf("%" PRId64 "\n", value);
(6)对象语义和值语义
值语义:值语义是指对象的拷贝与原对象无关,拷贝之后就与原对象脱离关系,彼此独立互不影响(深拷贝)。C++中的内置类型都是值语义,比如说int。值语义的一个巨大好处是生命期管理很简单。
对象语义:对象语义指的是面向对象意义下的对象。
对象拷贝要么是禁止的(Noncopyable) ,要么一个对象被系统标准的复制方式复制后,与被复制的对象之间依然共享底层资源,对任何一个的改变都将改变另一个(浅拷贝)。比如,Thread 是对象语义,拷贝 Thread 是无意义的,也是被禁止的:因为 Thread 代表线程,拷贝一个Thread对象并不能让系统增加一个一模一样的线程。
对象语义对象生命期不容易控制,一般通过智能指针来解决。智能指针实际上是将对象语义转化为值语义,利用局部对象(智能指针)的确定性析构,智能指针包括auto_ptr, shared_ptr, weak_ptr, scoped_ptr。