Higgs实习记录

思考

  1. 程序实现的时候要将代码逻辑和业务结合起来,比如说LoadParam中给定一个仓位,在db文件中查询相距最近的仓位对应的TradeParam,并不是单单去寻找唯一一个Param,那样的话一天就只会做一次交易,而是对于数据中每一个时间段都去寻找一个最近的仓位对应的TradeParam。
  2. 遇到非技术性问题先问deepseek,整理文档或代码时把问题整理到一起再问。

技术

多线程

线程是程序中的独立执行流,属于同一进程内的多个线程 共享进程的内存空间​(如全局变量、堆内存),但每个线程拥有 独立的栈空间和执行上下文​(如寄存器状态、程序计数器)。

线程 vs 进程:

特征 进程 线程
资源分配 操作系统资源分配的基本单位(独立内存空间) 进程内的执行单元(共享进程内存)
切换开销 高(涉及虚拟内存、文件描述符等切换) 低(仅切换栈和寄存器)
创建销毁 通过 fork() 创建,系统调用消耗大 通过 pthread_create() 创建,消耗小
独立性 相互隔离,一个进程崩溃不影响其他进程 共享内存,一个线程崩溃可能导致整个进程终止
通信方式 管道、消息队列、共享内存、Socket 等 直接读写共享变量(需同步机制)
CPU调度 进程是 CPU 资源分配的基本单位 线程是 CPU 调度的基本单位

数据对齐

内存对齐的核心规则

  1. 成员对齐规则:每个成员的起始地址必须是其自身大小的整数倍(例如 int 需4字节对齐,double 需8字节对齐)。
  2. 结构体填充规则:结构体总大小必须是其最大成员大小的整数倍。
  3. 成员顺序影响:成员顺序不同会导致填充字节的数量变化,进而影响总内存占用。
struct TestA{
    char a;
    int b;
    char c;
    double d;
};

内存布局​(假设默认8字节对齐):

  • char a占1字节,偏移量0。
  • int b需4字节对齐,因此在a后填充3字节(偏移量4-7)。
  • char c占1字节,偏移量8。
  • double d需8字节对齐,因此在c后填充7字节(偏移量16-23)。
  • ​总大小:1+3(填充)+4+1+7(填充)+8 = ​24字节。
struct TestB{
    char a;
    char c;
    int b;
    double d;
};

内存布局:

  • char a和char c共占2字节,偏移量0-1。
  • int b需4字节对齐,因此在c后填充2字节(偏移量4-7)。
  • double d直接对齐到偏移量8(无需填充)。
  • ​总大小:1+1+2(填充)+4+8 = ​16字节。
struct TestC{
    char a;
    char c;
    double d;
    char b;
};

内存布局:

  • char a和char c共占2字节,偏移量0-1。
  • double d需8字节对齐,因此在c后填充6字节(偏移量8-15)。
  • char b占1字节,偏移量16。
  • 结构体总大小需是最大成员double的倍数(8字节),因此在b后填充7字节。
  • ​总大小:1+1+6(填充)+8+1+7(填充) = ​24字节。

设计模式

Impl设计模式
  1. 接口与实现解耦:
    • 隐藏私有细节:通过将类的私有成员封装在一个独立的实现类中,头文件仅暴露公共接口,避免内部实现细节泄露。
    • 降低耦合性:接口类仅持有指向实现类的指针(unique_ptr或shared_ptr),实现类的修改不会影响接口类的用户代码。
  2. 减少编译依赖与提升编译速度
    • 最小化头文件修改影响:当实现类的内部逻辑变更时,只需重新编译实现文件(.cpp),而非所有包含原头文件的代码,显著减少大型项目的编译时间。
    • 避免头文件污染:减少头文件中的依赖项(如第三方库或复杂模板),简化编译流程。
  3. 增强二进制兼容性
    • 动态库接口稳定:在动态库开发中,接口类的内存布局不会因实现类的成员增减而改变,客户端无需重新编译即可兼容新版本。
  4. 资源管理与安全性
    • 自动内存管理:结合智能指针(如unique_ptr),可自动释放实现类对象,避免内存泄漏。
    • 防止悬垂指针:智能指针确保指针始终有效或为空,规避非法访问风险。
  5. 支持灵活的实现扩展
    • 多态性与桥接模式:可通过替换不同的实现类(如平台相关代码),动态切换功能模块,符合开闭原则。
    • 惰性初始化优化:实现类可按需初始化,提升资源利用率6。
Adapter设计模式

Adapter模式通过创建一个中间层(适配器类),将一个类的接口转换为客户端期望的另一个接口,从而解决接口不兼容的问题。其核心角色包括:

  1. ​Target(目标接口)​:客户端期望的接口。
  2. ​Adaptee(被适配者)​:需要被适配的现有类或接口。
  3. ​Adapter(适配器)​:将Adaptee的接口转换为Target接口的中间类 Ops Adapter包括td(trade data) adapter和md(market data) adapter,主要作用是屏蔽来自不同券商的api接口的差异,并统一适配到Higgs Ops interface定义的接口,以便于上层策略模块调用。
工厂模式

通过引入“工厂类”替代直接使用 new 关键字创建对象,将对象的创建逻辑集中管理,客户端仅需与工厂接口交互,无需关心具体类的实例化细节:

  1. 解耦:分离对象的创建与使用,降低模块间的依赖。
  2. ​封装:隐藏复杂的初始化逻辑(如数据库连接、配置解析),提供统一的创建接口。
  3. ​扩展性:支持新增产品类型时无需修改客户端代码,符合“开闭原则”。

    数据结构

std::set

std::set 是基于 ​红黑树​(一种自平衡二叉搜索树)实现的。红黑树的高度始终保持在 O(log n) 级别,因此所有基于树遍历的操作(如查找、插入、删除)的时间复杂度均为对数级别。

  • 需要频繁进行范围查询(如 lower_bound、upper_bound)时,std::set 的高效查找特性非常有用。
  • 若仅需判断元素是否存在,优先使用 std::unordered_set(查找时间复杂度 O(1))。
  • 若使用 std::lower_bound 在已排序的 vector 中查找,时间复杂度同样是 O(log n),但需要手动保证容器有序。若 vector 无序,需先排序(O(n log n)),再使用 lower_bound。