用 class,但只暴露必要接口;事件需封装时间逻辑如 isOverdue()、isToday(),字段私有,用 std::chrono::time_point 和 std::chrono::minutes,重复规则用 std::optional<RepeatRule>。

事件对象该用 struct 还是 class?关键看要不要封装逻辑
直接说结论:用 class,但只暴露必要接口。事件不是纯数据容器,它得自己算是否“已过期”、是否“在今天内”,否则每次提醒检查都要在外面重复写时间比较逻辑。
常见错误是把所有字段设为 public,结果后续加重复检测或时区支持时,散落在各处的 event.start_time 调用全得改。正确做法是把时间字段设为 private,提供 isOverdue()、isToday() 这类语义化接口。
-
start_time和end_time用std::chrono::system_clock::time_point,别用time_t或字符串——后者没法直接比较、易出时区错 - 别存“提醒提前量”为整数分钟,而要存
std::chrono::minutes类型,避免单位混淆(比如误把 30 当成秒) - 如果支持重复事件,
repeat_rule字段类型建议用std::optional<RepeatRule>,而不是 magic number(如 -1 表示不重复)
提醒对象为什么不能只是“触发一个函数”?
提醒不是简单回调,它得能被暂停、取消、重试、标记已读。用裸函数指针或 std::function<void()> 会丢失上下文和状态。
典型场景:用户点击“稍后提醒”,程序得在 10 分钟后再次弹窗——这需要提醒对象自己持有下次触发时间,并能重新注册到调度器。所以提醒对象必须是完整生命周期可控的实体。
立即学习“C++免费学习笔记(深入)”;
- 提醒类至少要有
trigger()、dismiss()、snooze(std::chrono::minutes)三个方法 - 内部保存一个
std::weak_ptr<Event>,防止事件被删后提醒还试图访问野指针 - 别把 UI 弹窗逻辑塞进提醒类;用观察者模式通知 UI 层,保持解耦
std::map 还是 std::unordered_map 存待提醒事件?看你怎么查
如果按“下一次提醒时间”排序遍历(比如每秒检查最早该触发的那条),必须用 std::map<time_point, std::vector<Reminder*>>。用 unordered_map 就得每次 std::min_element 扫全表,O(n) 开销扛不住。
但注意:time_point 的精度可能导致键冲突(同一毫秒多个事件)。别直接用 time_point 当 key,而是用 std::pair<time_point, size_t>,用递增序号破歧义。
- 插入新提醒时,先
lower_bound定位位置再插入,避免全表重排 - 触发后记得从 map 中 erase 对应项——漏删会导致重复提醒
- 如果支持“按事件 ID 查提醒”,额外建一张
std::unordered_map<std::string, Reminder*>反查表,别硬搜
跨平台时间处理最容易栽在时区和夏令时上
本地时间 ≠ UTC,而系统 API 返回的 system_clock::now() 是 UTC。如果你用 std::put_time 格式化显示却没设本地 facet,或者用 std::mktime 解析用户输入的“明天 9:00”却忽略 tm_isdst,提醒就会偏移一小时。
最省事又可靠的做法:所有存储和计算用 UTC,仅在显示和用户输入时做一次本地化转换。
- 用户输入“2025-04-05 14:00”,用
date::local_days+date::make_time(需引入<date/date.h>)转成 UTCtime_point - 别依赖
std::localtime,它不是线程安全的;改用std::gmtime_r或 C++20 的std::chrono::zoned_time - 测试时强制把系统时区切到
Asia/Shanghai和America/New_York各跑一遍,别只在开发机默认时区验证


















