2026.02¶
20260209¶
今天开始写自己想做的第一个小项目了。
之前写前端项目的时候,不需要每次手动 source .env,因为前端工具链( Vite、Creat React App、Next.js )和 Node.js 会自动读取项目根目录下的 .env 文件。写 C++ 项目发现 .env 不会自动被项目代码读到。
.env 文件里的变量默认不写 export,这时候它们是 shell 变量(还不是环境变量)。
从操作系统的角度解释:Shell 本身就是一个普通进程,各种变量存在这个进程的用户态内存。source .env 只对当前 shell 进程有效。在 shell 里运行一个程序的本质是 fork + exec。新程序拷贝了父进程的寄存器和内存。父进程被关闭了,那么寄存器和内存就被清空了。
Shell 变量是针对当前 shell session 的,如果想要在 shell 的子进程里用上 .env 里的变量,每次重启一个 shell 都需要:
如果只是 source .env,变量仅当前 shell 可用。
set -ac的作用是自动 export 之后定义的所有变量。set +a 是关闭自动 export 功能。这样才能把 shell 变量变成子进程也能感知到的环境变量。
20260202¶
Read:为啥 C++ 程序会在 main 函数之前崩溃? 这篇文章讲的是静态初始化顺序问题。
不同 .cpp 文件里,全局变量(包括 static 全局变量)的动态初始化顺序是未定义的。globalDB 在构造时会调用 globalLogger.log(),但有可能 globalLogger 还没初始化完呢。
Scott Meyers 提出了一个经典解决方案:每个全局对象改成用函数包装的静态变量。
// logger.cpp
Logger& getLogger() {
static Logger instance; // 函数内的静态变量,第一次调用时才初始化
return instance;
}
// database.cpp
Database& getDatabase() {
static Database instance; // 函数内的静态变量,第一次调用时才初始化
return instance;
}
Database::Database() {
getLogger().log("DB Init"); // 安全!保证Logger先初始化
}
static Logger instance 会在第一次执行到这行代码时初始化。这就把初始化时机推迟到了第一次使用时,谁先用谁先初始化。