使用Eigen库静态变量作为默认参数导致C++ Camera类构造段错误
问题原因
你猜测的方向完全正确,问题根源就是不同编译单元的静态变量初始化顺序未定义,也就是C++中著名的「静态初始化顺序问题(Static Initialization Order Fiasco)」。
- C++标准仅保证同一编译单元(单个.cpp文件)内的静态/全局变量按照定义的先后顺序初始化,不同编译单元之间的静态变量初始化顺序没有任何约束,完全由编译器的实现决定。
- 你的
Camera::DEFAULT_LOC是定义在camera.cpp中的静态成员变量,如果你创建Camera对象的代码位于其他编译单元,且该编译单元的静态初始化逻辑比camera.cpp的先执行,那么调用构造函数时DEFAULT_LOC还未完成初始化,指向的是未被正确赋值的内存。访问未初始化的Eigen对象(内部包含对齐要求的矩阵数据)有极高概率触发段错误,刚好匹配你遇到的90%崩溃概率的现象。 - 你改用
Transform::Identity()作为默认参数后问题消失,是因为Transform::Identity()是调用构造函数时当场生成的临时对象,不存在提前初始化的问题,临时对象的生命周期会覆盖整个构造执行流程,不会出现访问非法内存的情况。
可行解决方案
- 直接使用临时对象作为默认参数:就是你目前采用的方案,实现最简单,没有额外的适配成本。
- 用函数内静态变量包装默认常量:如果你需要保留自定义的
DEFAULT_LOC默认值,可以用Meyer单例的写法规避初始化顺序问题:
// 在Camera类内部替换原来的静态常量声明为静态成员函数 class Camera { public: // 替换原来的 const static Transform DEFAULT_LOC; 为下面的函数 static const Transform& DEFAULT_LOC() { static const Transform instance((Eigen::Matrix4f() << 0, 0, 1, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 1).finished()); return instance; } // 构造函数的默认参数改为调用函数 Camera(const CameraDescription &desc, const Transform &loc=DEFAULT_LOC()); };
这种写法中,函数内的静态变量会在第一次调用DEFAULT_LOC()时才完成初始化,不管调用方位于哪个编译单元,都能保证拿到已经初始化完成的合法对象。
3. 延后Camera对象的创建时机:确保你创建Camera对象的逻辑在main函数执行之后再执行,main函数启动时所有全局静态变量已经完成初始化,不会出现顺序问题。
内容的提问来源于stack exchange,提问作者ex.nihil
相关产品推荐
相关产品推荐

