C++编程-程序初始化注意事项

mac2026-04-14  2

概述

都是从"稀奇古怪的问题"开始的,博主早年开始写C++程序时,最鄙视用全局变量的行为,对单件也很不屑,后来有段时间却突然觉得这些用很方便呢还,在到后来,猛然发觉那些个代码带来的坑真不止一二三啊,尤其是碰上嵌入式上的运行异常排查!

静态成员

某天开始,还惯用上了用“指向自身的聚合”,这省了再单独定义一个类来维护对象集合的代码,但却带来了"新的问题"!下边是从某次异常调试中演变出来一个简化的小测例,当时主要用来测试为啥😩在了Qhash的代码行!

简单测例

/** 派生的QHahs为方便查看其创建时间 **/ //.h class CHashCustom : public QHash<int, int> { public: explicit CHashCustom(); }; /** 派生的QHahs为方便查看其创建时间 **/ //.cpp CHashCustom::CHashCustom() { qDebug() << "[Create]-HashCustom!"; } /** 含静态成员的类-被设计为全局对象 **/ //.h #ifndef _RIVER_GLOBALOBJ_H_ #define _RIVER_GLOBALOBJ_H_ #include "hashcustom.h" class CGlobalObj { public: CGlobalObj(); private: static CHashCustom ms_hashData; }; #endif // _RIVER_GLOBALOBJ_H_ /** 含静态成员的类-被设计为全局对象 **/ //.cpp //create-Order-B global object CGlobalObj g_Object; //create-Order-A static attribute CHashCustom CGlobalObj::ms_hashData; CGlobalObj::CGlobalObj() { qDebug() << "[Create]-GlobalObj!"; //using static attribute if (ms_hashData.end() == ms_hashData.find(100)) { qDebug() << "[Ok] can using ms_hashData now!"; } }

基本测试结果

代码顺序测试结果create-Order-Bcreate-Order-A程序崩溃 打印信息与崩溃位置如下 create-Order-Acreate-Order-B正常运行 方案1 - 异常分析 异常代码位置如下 //qbasicatomic.h //D:\Qt\4.8.6\include\QtCore inline bool operator!=(int value) const { return _q_value != value; //最后的异常堆栈处 } //相关的代码 #if defined(QT_ARCH_WINDOWS) || defined(QT_ARCH_WINDOWSCE) union { // needed for Q_BASIC_ATOMIC_INITIALIZER volatile long _q_value; }; #else volatile int _q_value; #endif

基本结论

早知道普通成员变量是在类构造函数执行前就创建好了,但再深入的不了解。静态成员ms_hashData属于类,为所有对象公共持有,其初始化时机与普通成员不一样。只静态成员变量必须在cpp中被执行(这分明是全局对象的创建套路啊!)…静态对象、全局对象都是先于main函数初始化的,这毋庸置疑,但是多个静态成员和全局变量之间,其被执行顺序是不确定的,这于编译顺序有关(基于上述测试代码,哪行代码写在前边,编译后就先执行器初始化),与运行平台有关(实际程序中的问题时在嵌入式Linux上运行发现的,同样的代码在WinPc上运行无误!)。根本结论 静态成员ms_hashData与其所属类CGlobalObj的创建先后顺序是不完全确定的。如果CGlobalObj不是在mian函数后自然创建的,而是通过某全局对象触发的创建过程,就尴尬了,CGlobalObj创建函数时,属于它的ms_hashData还木创建,假如此时在CGlobalObj构造函数中使用ms_hashData,将可能发生“嘣嘣嘣…”,挂啦…

反人类测例

基于上边Qhash的测试,想再写个更通用的测例,结果"神奇的事情"发生了,搞不死啊直接。接下来,QtCreater竟默许了空指针调用,简直是反人类啊!且看代码:

/************全局对象 obj-a****************/ //.h class CObjectA { public: CObjectA(); private: static CObjectB ms_ObjectB; }; /************全局对象 obj-a****************/ //.cpp CObjectA g_ObjeA; //全局 CObjectB CObjectA::ms_ObjectB; //静态成员 CObjectA::CObjectA() { qDebug() << "Create A while " << "ms_ObjectB Addr:" << &ms_ObjectB; qDebug() << "ms-FindB1" << ms_ObjectB.FindB1(); qDebug() << "ms-FindB2" << ms_ObjectB.FindB2(); } /************ 静态成员 ms-obj-b ****************/ //.h class CObjectB { public: CObjectB(); public: int FindB1(); int FindB2(); private: CObjectC *m_pObjC; }; /************ 静态成员 ms-obj-b ****************/ //.cpp CObjectB::CObjectB() { qDebug() << "Create B"; m_pObjC = NULL; m_pObjC = new CObjectC(); } int CObjectB::FindB1() { return 0x0B; } int CObjectB::FindB2() { return m_pObjC->FindC(); } /************ 普通成员 ms-obj-c ****************/ //.h class CObjectC { public: CObjectC(); public: int FindC(); }; /************ 普通成员 ms-obj-c ****************/ //.cpp CObjectC::CObjectC() { qDebug() << "Create C"; } int CObjectC::FindC() { qDebug() << QString("Execute ObjectC(%1)->FindC()") .arg((int)this, 8, 16, QChar('0')); return 0x0C; }

运算结果如下:

猜测 可能是编译运行环境的优化参数导致的,此处并未做深究。需要明确的时,我们不允许发生这种情况。 至于为什么static QHash的就死掉了,可能更 volatile 可能与其不可优化性有关—

事后检讨

应该尽量避免创建全局对象,减少单体对象的使用。

使用全局变量的危害 1、发生初始化异常时,难以排查问题位置。 2、main函数前的创建较多时,对于开机耗时无法有效测试。 3、在全局变量中进行字符串读写时,可能会发生乱码问题,因为此时main函数中setCode并未设置

其他待整理2

普通成员变量是在所属构造函数执行前就已经初始化好的- 我们可以定义写简单类的成员对象-然后依次打印创建顺序…

最新回复(0)