3.时间类的实现-c++ linux编程:从0实现muduo库系列

重点内容

这一讲的内容主要是时间类的实现,但时间类面试很少被问到,所以这里我们主要讲CMakeLists.txt如何生成库文件,以及make install的时候如何把对应的库文件和头文件安装到指定目录。

视频讲解:《C++Linux编程进阶:从0实现muduo C++网络框架系列》-第3讲.时间类的实现

主要改动

首先创建第三节课的目录并复制前1节课的内容:

cp -r  lesson2 lesson3

muduo网络库框架:

  • 实现:base/Timestamp.h/cc
  • 实现:base/Date.h/cc
  • 实现:base/TimeZone.h/cc

muduo网络库测试范例

  • examples/test_timestamp.cc
  • examples/test_date.cc
  • examples/test_timezone.cc

CMakeLists.txt

  • base/CMakeLists.txt
  • examples/CMakeLists.txt

1 CMake生成库文件以及make install安装库文件和头文件

1.1 CMakeLists.txt主要改动

重点掌握

具体细节内容:

1.base/CMakeLists.txt

add_library(mymuduo_base ${base_SRCS}) 生成mymuduo_base静态库

target_link_libraries(mymuduo_base pthread) mymuduo_base依赖pthread库,默认优先依赖动态库

安装文件:

  • 安装库文件:install(TARGETS mymuduo_base DESTINATION /usr/local/lib)
  • 安装头文件:install(FILES ${base_HEADERS} DESTINATION /usr/local/include/mymuduo/base)

2.examples/CMakeLists.txt

target_link_libraries(test_date mymuduo_base) ,这里链接mymuduo_base这个库文件,可以注释后测试报什么错。

1.2 CMake语法讲解

1.2.1 add_library生成库文件

# 方式1:不指定类型,使用默认行为(静态库)
add_library(mylib src1.cpp src2.cpp)

# 方式2:明确指定类型
add_library(mylib STATIC src1.cpp src2.cpp)  # 静态库
add_library(mylib SHARED src1.cpp src2.cpp)  # 动态库

# 方式3:通过变量控制默认行为
set(BUILD_SHARED_LIBS ON)
add_library(mylib src1.cpp src2.cpp)  # 将生成动态库

1.2.2 target_link_libraries

1. 基本语法解析

target_link_libraries(test_date          # 目标程序,这里也可以是库
                     mymuduo_base)       # 需要链接的库

2.实际使用示例

# 1. 基本用法
add_executable(test_date test_date.cc)
target_link_libraries(test_date mymuduo_base)

# 2. 链接多个库
target_link_libraries(test_date
    mymuduo_base
    pthread
    rt
)

3.更复杂的语法 -目前没有必要深究

# 三种链接属性
target_link_libraries(test_date 
    PRIVATE mymuduo_base    # 私有链接:只有test_date使用
    PUBLIC other_lib        # 公有链接:test_date和链接test_date的目标都能使用
    INTERFACE util_lib      # 接口链接:只有链接test_date的目标能使用
)

后续遇到再深究。

1.2.3 install安装文件

1.安装库文件

insinstall(TARGETS mymuduo_base   #库文件
  DESTINATION 
  /usr/local/lib  # 安装位置
  )

2.安装头文件

# 先选定要安装的头文件
set(base_HEADERS
    Version.h
    Types.h
    copyable.h
    noncopyable.h
    StringPiece.h
    Timestamp.h
    Date.h
    TimeZone.h
)

install(FILES ${base_HEADERS}   # 要安装的头文件
  DESTINATION 
  /usr/local/include/mymuduo/base  # 安装位置
)

为什么不用lesson2的file命令?

file(GLOB HEADERS "*.h")

是因为有些头文件只是项目内部使用,可以不用对外提供,但file命令通过 *.h 匹配,则会选中所有的头文件。

2 时间类设计

2.1 涉及的时间函数

我来整理一下这些文件中使用的时间相关函数:

2.1.1 gettimeofday获取当前时间,精确到微秒

// 原型
int gettimeofday(struct timeval *tv, struct timezone *tz);

// 说明:获取当前时间,精确到微秒
struct timeval {
    time_t      tv_sec;   // 秒
    suseconds_t tv_usec;  // 微秒
};

// 使用示例
struct timeval tv;
gettimeofday(&tv, nullptr);
int64_t seconds = tv.tv_sec;
int64_t microseconds = tv.tv_usec; 

2.1.2 gmtime_r将time_t转换为UTC时间的tm结构体

// 原型
struct tm *gmtime_r(const time_t *timep, struct tm *result);

// 说明:将time_t转换为UTC时间的tm结构体,线程安全版本
struct tm {
    int tm_sec;    // 秒 [0,61]
    int tm_min;    // 分 [0,59]
    int tm_hour;   // 时 [0,23]
    int tm_mday;   // 日 [1,31]
    int tm_mon;    // 月 [0,11]
    int tm_year;   // 年 (从1900年开始)
    int tm_wday;   // 星期 [0,6] (星期日=0)
    int tm_yday;   // 一年中的第几天 [0,365]
    int tm_isdst;  // 夏令时标志
};

// 使用示例
time_t seconds = time(nullptr);
struct tm tm_time;
gmtime_r(&seconds, &tm_time);
printf("%d-%02d-%02d %02d:%02d:%02d\n",
    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);

2.1.3mktime将本地时间tm结构体转换为time_t

// 原型
time_t mktime(struct tm *tm);

// 说明:将本地时间tm结构体转换为time_t

// 使用示例
struct tm local_time = {0};
local_time.tm_year = 2024 - 1900;  // 年份从1900开始
local_time.tm_mon = 2 - 1;         // 月份从0开始
local_time.tm_mday = 23;           // 日期
local_time.tm_hour = 15;           // 小时
local_time.tm_min = 30;            // 分钟
local_time.tm_sec = 0;             // 秒
time_t t = mktime(&local_time);

2.1.4 timegm将UTC时间tm结构体转换为time_t

// 原型
time_t timegm(struct tm *tm);

// 说明:将UTC时间tm结构体转换为time_t(与mktime相对)

// 使用示例
struct tm utc_time = {0};
utc_time.tm_year = 2024 - 1900;
utc_time.tm_mon = 2 - 1;
utc_time.tm_mday = 23;
utc_time.tm_hour = 7;  // UTC时间
utc_time.tm_min = 30;
utc_time.tm_sec = 0;
time_t t = timegm(&utc_time);

2.1.5 time获取当前时间戳(秒级)

// 原型
time_t time(time_t *tloc);

// 说明:获取当前时间戳(秒级)

// 使用示例
time_t now = time(nullptr);

2.2 项目中的实际使用

这些函数在muduo网络库代码中的实际应用:

1.获取当前时间戳(微秒级):

这里返回微秒,后续使用时也可以将其转换为毫秒。

// Timestamp::now()的实现
Timestamp Timestamp::now()
{
    struct timeval tv;
    gettimeofday(&tv, nullptr);
    int64_t seconds = tv.tv_sec;
    return Timestamp(seconds * kMicroSecondsPerSecond + tv.tv_usec);
}

在EPollPoller::poll,定时器howMuchTimeFromNow时有使用。

2.时间格式化:

// Timestamp::toFormattedString的实现
std::string Timestamp::toFormattedString(bool showMicroseconds) const
{
    char buf[64] = {0};
    time_t seconds = static_cast<time_t>(microSecondsSinceEpoch_ / kMicroSecondsPerSecond);
    struct tm tm_time;
    gmtime_r(&seconds, &tm_time);
    
    snprintf(buf, sizeof(buf), "%4d%02d%02d %02d:%02d:%02d",
            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);
    return buf;
}

SystemInspector::overview打印系统信息时有使用 toFormattedString函数。

3.时区转换:

// TimeZone::toLocalTime的实现
struct tm TimeZone::toLocalTime(time_t seconds) const
{
    struct tm localTime;
    if (!data_)
    {
        gmtime_r(&seconds, &localTime);
        return localTime;
    }
    const Data::LocalTime* local = data_->findLocalTime(seconds);
    time_t localSeconds = seconds + local->gmtOffset;
    gmtime_r(&localSeconds, &localTime);
    return localTime;
}

日志格式化时 Logger::Impl::formatTime 有使用这些函数。

这些时间函数的使用需要注意:

  1. 线程安全性:优先使用 _r 后缀的函数(如gmtime_r)
  2. 时区处理:注意UTC时间和本地时间的转换
  3. 精度选择:根据需求选择秒级或微秒级函数
  4. 性能考虑:gettimeofday比time开销大,但精度更高

3. 整体时间类设计

3.1 时间类组件关系

3.2 时间戳处理流程

3.3 日期转换流程

3.4 时区转换流程

3.5 测试用例执行

3.5.1 Timestamp 测试

# 执行时间戳测试
./bin/test_timestamp

# 测试输出示例
=== 测试 Timestamp ===
当前时间: 2024-03-14 10:30:45.123456
格式化时间: 2024-03-14 10:30:45
时间差: 10.000000 seconds

3.5.2 Date 测试

# 执行日期测试
./bin/test_date

# 测试输出示例
=== 测试 Date ===
今天: 2024-03-14
星期: Thursday
儒略日: 2460293

3.5.3 TimeZone 测试

# 执行时区测试
./bin/test_timezone

# 测试输出示例
=== 测试 TimeZone ===
UTC: 2024-03-14 02:30:45
北京时间: 2024-03-14 10:30:45

4 章节总结

重点掌握:

使用add_library生成库文件

  • add_library(mymuduo_base ${base_SRCS})

使用target_link_libraries链接目标依赖的库文件

  • target_link_libraries(mymuduo_base pthread)

使用install安装库文件和头文件

  • install(TARGETS mymuduo_base DESTINATION /usr/local/lib)
  • install(FILES ${base_HEADERS} DESTINATION /usr/local/include/mymuduo/base)

了解内容:

  • 平时用gettimeofday是最多的,可以精确到微秒, 如果你只需要秒级别的函数则可以使用time(time_t *tloc)获取秒。
#项目##C++##投递实习岗位前的准备##c++后端##牛客创作赏金赛#
全部评论

相关推荐

评论
点赞
2
分享

创作者周榜

更多
牛客网
牛客企业服务