Java 静态初始化块的 C++ 实现:Static 局部结构体 VS 静态变量 + Lambda

张开发
2026/4/23 17:33:46 15 分钟阅读
Java 静态初始化块的 C++ 实现:Static 局部结构体 VS 静态变量 + Lambda
考虑这样一种典型场景我需要设计一个名为AppDirLocator的类这个类包含一些程序要使用的重要文件夹的路径例如应用程序目录m_appDirPath因们它们需要在程序各处使用且是静态不变的为了使用方便我希望把它们设计成静态成员变量然后通过静态的 getter 去获取它们就像这样AppDirLocator::appDirPath()。但是这些路径需要在程序启动时根据一定的逻辑才能确定下来也就是需要一个地方可以执行一段初始化代码。如果是在 Java 中最适宜执行这段逻辑的地方是静态初始化块但是在 C 中没有静态初始化块这种机制所以需要找到一种迂回方案。1. 初始版本最初使用的是一版常规实现把各个目录和它们的路径设为 static 的成员变量通过 static 的 getter 获取。然后在一个私有的静态的方法里实现它们的初始化方法即initIfNot()方法为了实现懒加载需要在每一个 getter 中先执行一次initIfNot()方法这很不优雅。头文件#ifndefAPPDIRSLOCATOR_H#defineAPPDIRSLOCATOR_H#includeQDir#includeQStringnamespacemyapp{classAppDirsLocator{public:staticQStringappDirPath();staticQStringappDataDirPath();staticQStringappLocalDataDirPath();staticQDirappDir();staticQDirappDataDir();staticQDirappLocalDataDir();private:staticboolm_inited;staticQString m_portableAppDataDirName;staticQString m_portableUserDataDirName;staticvoidinitIfNot();staticQString m_appDirPath;staticQString m_appDataDirPath;staticQString m_appLocalDataDirPath;staticQDir m_appDir;staticQDir m_appDataDir;staticQDir m_appLocalDataDir;};}#endif//APPDIRSLOCATOR_H源文件#includeAppDirsLocator.h#includeQCoreApplication#includeQStandardPaths#includeutils/pathutils.husingnamespacemyapp;boolAppDirsLocator::m_initedfalse;QString AppDirsLocator::m_portableAppDataDirNameapp_files;QString AppDirsLocator::m_portableUserDataDirNameuser_files;QString AppDirsLocator::m_appDirPath;QString AppDirsLocator::m_appDataDirPath;QString AppDirsLocator::m_appLocalDataDirPath;QDir AppDirsLocator::m_appDir;QDir AppDirsLocator::m_appDataDir;QDir AppDirsLocator::m_appLocalDataDir;voidAppDirsLocator::initIfNot(){if(m_inited)return;m_appDirPathPathUtils::parentDirPath(QCoreApplication::applicationFilePath());m_appDirQDir(m_appDirPath);qInfo()app dir path: m_appDirPath;// determine app data location ( app level data )QStringportableAppDataDirPath(m_appDirPath/m_portableAppDataDirName);if(QDir(portableAppDataDirPath).exists()){// Config folder in app/.m_appDataDirPathPathUtils::cleanPath(portableAppDataDirPath);}else{m_appDataDirPathQStandardPaths::writableLocation(QStandardPaths::AppDataLocation);}m_appDataDirQDir(m_appDataDirPath);qInfo()app data dir path: m_appDataDirPath;// determine app local data location ( user level data )QStringportableUserDataDirPath(m_appDirPath/m_portableUserDataDirName);if(QDir(portableUserDataDirPath).exists()){// Config folder in app/.m_appLocalDataDirPathPathUtils::cleanPath(portableUserDataDirPath);}else{m_appLocalDataDirPathQStandardPaths::writableLocation(QStandardPaths::AppLocalDataLocation);}m_appLocalDataDirQDir(m_appLocalDataDirPath);qInfo()user data dir pathm_appLocalDataDirPath;m_initedtrue;}QStringAppDirsLocator::appDirPath(){initIfNot();returnm_appDirPath;}QStringAppDirsLocator::appDataDirPath(){initIfNot();returnm_appDataDirPath;}QStringAppDirsLocator::appLocalDataDirPath(){initIfNot();returnm_appLocalDataDirPath;}QDirAppDirsLocator::appDir(){initIfNot();returnm_appDir;}QDirAppDirsLocator::appDataDir(){initIfNot();returnm_appDataDir;}QDirAppDirsLocator::appLocalDataDir(){initIfNot();returnm_appLocalDataDir;}2. Static 局部结构体该方案将各个目录和它们的路径封装进一个结构体Dirs中在结构体的构造方法中实现初始化它们的逻辑。然后重点在于在获取这个结构体实例的静态方法中将 Dirs 的实例声明为静态局部变量static Dirs dirs这确保了 dirs 实例只会被初始化一次且是线程安全的。头文件#ifndefAPPDIRSLOCATOR_H#defineAPPDIRSLOCATOR_H#includeQDir#includeQStringnamespacemyapp{classAppDirsLocator{public:staticQStringappDirPath();staticQStringappDataDirPath();staticQStringappLocalDataDirPath();staticQDirappDir();staticQDirappDataDir();staticQDirappLocalDataDir();private:// 把所有路径包成一个结构体只初始化一次structDirs{QString m_portableAppDataDirNameapp_files;QString m_portableUserDataDirNameuser_files;QString m_appDirPath;QString m_appDataDirPath;QString m_appLocalDataDirPath;QDir m_appDir;QDir m_appDataDir;QDir m_appLocalDataDir;// 构造函数里统一初始化Dirs();};// 获取 Dirs 的静态方法staticconstDirsinstance();};}#endif//APPDIRSLOCATOR_H源文件#includeAppDirsLocator.h#includeQCoreApplication#includeQStandardPaths#includeutils/pathutils.husingnamespacemyapp;AppDirsLocator::Dirs::Dirs(){m_appDirPathPathUtils::parentDirPath(QCoreApplication::applicationFilePath());m_appDirQDir(m_appDirPath);qInfo()app dir path: m_appDirPath;// determine app data location ( app level data )QStringportableAppDataDirPath(m_appDirPath/m_portableAppDataDirName);if(QDir(portableAppDataDirPath).exists()){// Config folder in app/.m_appDataDirPathPathUtils::cleanPath(portableAppDataDirPath);}else{m_appDataDirPathQStandardPaths::writableLocation(QStandardPaths::AppDataLocation);}m_appDataDirQDir(m_appDataDirPath);qInfo()app data dir path: m_appDataDirPath;// determine app local data location ( user level data )QStringportableUserDataDirPath(m_appDirPath/m_portableUserDataDirName);if(QDir(portableUserDataDirPath).exists()){// Config folder in app/.m_appLocalDataDirPathPathUtils::cleanPath(portableUserDataDirPath);}else{m_appLocalDataDirPathQStandardPaths::writableLocation(QStandardPaths::AppLocalDataLocation);}m_appLocalDataDirQDir(m_appLocalDataDirPath);qInfo()user data dir pathm_appLocalDataDirPath;}// 静态局部变量C11 自动保证线程安全 只初始化一次constAppDirsLocator::DirsAppDirsLocator::instance(){staticDirs dirs;returndirs;}QStringAppDirsLocator::appDirPath(){returninstance().m_appDirPath;}QStringAppDirsLocator::appDataDirPath(){returninstance().m_appDataDirPath;}QStringAppDirsLocator::appLocalDataDirPath(){returninstance().m_appLocalDataDirPath;}QDirAppDirsLocator::appDir(){returninstance().m_appDir;}QDirAppDirsLocator::appDataDir(){returninstance().m_appDataDir;}QDirAppDirsLocator::appLocalDataDir(){returninstance().m_appLocalDataDir;}3. 静态变量 Lambda这仍旧是依靠“静态局部变量”实现的静态初始化与前一个方案分装为统一结构体不同这种方案是“各个击破”针对每一个变量使用一个静态局部变 一个 Lambda 封装初始化逻辑以m_appDirPath为例头文件namespacemyapp{classAppDirsLocator{public:staticQStringappDirPath();};}源文件QStringAppDirsLocator::appDirPath(){// 静态初始化块C 版staticconstQString path[]{// 这里写任意复杂初始化逻辑returnPathUtils::parentDirPath(QCoreApplication::applicationFilePath());}();// 立即执行returnpath;}4. 优劣对比两个方案都能实现近似于“静态初始化块”的功能也都是懒加载模式不必考虑在何处显示地初始化。它们的之间的差异主要是Static 局部结构体一次性分装了所有需要静态初始化的成员只需要一个静态局部变量整体上更清晰、条理非常适合要同时初始化多个成员且它们彼此之间在初始化时还要相互依赖的场景本例就是这种情形静态变量 Lambda 是一个成员一个成员的单独进行静态初始化当成员数量较少且彼此之间孤立无关联时可以考虑使用但一但数量变多且成员之间在初始化时会相互关联时那就不适合了。

更多文章