muduo网络库学习笔记(1):Timestamp类

时间戳一般用来唯一地标识某一刻的时间,通常是指格林威治时间1970年01月01日00时00分00秒(北京时间1970年01月01日08时00分00秒)起至现在的总毫秒数。

muduo网络库对时间戳Timestamp类也进行了封装。

Timestamp类类图:
muduo网络库学习笔记(1):Timestamp类_第1张图片

由类图可知,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。

你可能感兴趣的:(muduo)