作用域(Scope)和生命周期(Lifetime)是 C++ 中的两个重要概念。作用域决定了变量在程序中的可见性,而生命周期决定了变量在内存中存在的时间。

作用域的基本概念

全局作用域

#include <iostream>
 
// 全局变量 - 全局作用域
int globalVar = 100;
const int globalConst = 200;
static int staticGlobal = 300;  // 文件作用域
 
// 全局函数
void globalFunction() {
    std::cout << "全局函数被调用" << std::endl;
}
 
// 命名空间作用域
namespace MyNamespace {
    int namespaceVar = 400;
    
    void namespaceFunction() {
        std::cout << "命名空间函数被调用" << std::endl;
    }
}
 
int main() {
    std::cout << "=== 全局作用域演示 ===" << std::endl;
    
    // 访问全局变量
    std::cout << "globalVar: " << globalVar << std::endl;
    std::cout << "globalConst: " << globalConst << std::endl;
    std::cout << "staticGlobal: " << staticGlobal << std::endl;
    
    // 调用全局函数
    globalFunction();
    
    // 访问命名空间中的变量和函数
    std::cout << "MyNamespace::namespaceVar: " << MyNamespace::namespaceVar << std::endl;
    MyNamespace::namespaceFunction();
    
    // 修改全局变量
    globalVar = 150;
    std::cout << "修改后的 globalVar: " << globalVar << std::endl;
    
    return 0;
}

局部作用域

#include <iostream>
 
int globalVar = 10;  // 全局变量
 
void demonstrateLocalScope() {
    int localVar = 20;  // 函数局部变量
    
    std::cout << "=== 局部作用域演示 ===" << std::endl;
    std::cout << "函数内 localVar: " << localVar << std::endl;
    std::cout << "函数内 globalVar: " << globalVar << std::endl;
    
    // 块作用域
    {
        int blockVar = 30;  // 块局部变量
        int localVar = 40;  // 隐藏外层的 localVar
        
        std::cout << "\n块内 blockVar: " << blockVar << std::endl;
        std::cout << "块内 localVar: " << localVar << std::endl;  // 40,不是20
        std::cout << "块内 globalVar: " << globalVar << std::endl;
        
        // 嵌套块
        {
            int nestedVar = 50;
            int blockVar = 60;  // 隐藏外层的 blockVar
            
            std::cout << "\n嵌套块内 nestedVar: " << nestedVar << std::endl;
            std::cout << "嵌套块内 blockVar: " << blockVar << std::endl;  // 60,不是30
        }
        // nestedVar 在这里不可访问
        
        std::cout << "\n回到外层块 blockVar: " << blockVar << std::endl;  // 30
    }
    // blockVar 在这里不可访问
    
    std::cout << "\n回到函数 localVar: " << localVar << std::endl;  // 20
}
 
void demonstrateForLoopScope() {
    std::cout << "\n=== for 循环作用域 ===" << std::endl;
    
    // C++98 风格
    int i;
    for (i = 0; i < 3; ++i) {
        std::cout << "C++98 风格 i: " << i << std::endl;
    }
    std::cout << "循环后 i: " << i << std::endl;  // i 仍然可访问
    
    // C++11 风格
    for (int j = 0; j < 3; ++j) {
        std::cout << "C++11 风格 j: " << j << std::endl;
    }
    // j 在这里不可访问
    
    // 范围 for 循环
    int arr[] = {1, 2, 3};
    for (int element : arr) {
        std::cout << "范围 for element: " << element << std::endl;
    }
    // element 在这里不可访问
}
 
int main() {
    demonstrateLocalScope();
    demonstrateForLoopScope();
    
    return 0;
}

作用域解析

#include <iostream>
 
int value = 100;  // 全局变量
 
namespace Outer {
    int value = 200;
    
    namespace Inner {
        int value = 300;
        
        void demonstrateScopeResolution() {
            int value = 400;  // 局部变量
            
            std::cout << "=== 作用域解析演示 ===" << std::endl;
            std::cout << "局部 value: " << value << std::endl;                    // 400
            std::cout << "Inner::value: " << Inner::value << std::endl;          // 300
            std::cout << "Outer::value: " << Outer::value << std::endl;          // 200
            std::cout << "全局 value: " << ::value << std::endl;                 // 100
            
            // 使用 using 声明
            {
                using Outer::value;  // 引入 Outer::value
                std::cout << "\nusing Outer::value 后:" << std::endl;
                std::cout << "value: " << value << std::endl;                    // 200
                std::cout << "::value: " << ::value << std::endl;                // 100
            }
            
            // 使用 using 指令
            {
                using namespace Outer;
                std::cout << "\nusing namespace Outer 后:" << std::endl;
                std::cout << "value: " << value << std::endl;                    // 400 (局部变量优先)
                // std::cout << "Outer::value: " << Outer::value << std::endl;  // 仍然可以显式访问
            }
        }
    }
}
 
class ScopeDemo {
    static int classVar;
    int memberVar;
    
public:
    ScopeDemo(int val) : memberVar(val) {}
    
    void memberFunction() {
        int localVar = 500;
        
        std::cout << "\n=== 类作用域演示 ===" << std::endl;
        std::cout << "局部变量 localVar: " << localVar << std::endl;
        std::cout << "成员变量 memberVar: " << memberVar << std::endl;
        std::cout << "静态成员变量 classVar: " << classVar << std::endl;
        std::cout << "全局变量 value: " << ::value << std::endl;
    }
    
    static void staticFunction() {
        // 静态函数只能访问静态成员
        std::cout << "静态函数中的 classVar: " << classVar << std::endl;
        // std::cout << memberVar;  // 错误!不能访问非静态成员
    }
};
 
int ScopeDemo::classVar = 600;  // 静态成员定义
 
int main() {
    Outer::Inner::demonstrateScopeResolution();
    
    ScopeDemo demo(700);
    demo.memberFunction();
    ScopeDemo::staticFunction();
    
    return 0;
}

C++ 对象生命周期详解

生命周期的基本概念

在C++中,对象的生命周期决定了对象何时被创建和销毁。主要有四种存储期:

  • 自动存储期(Automatic Storage Duration):栈上的局部变量
  • 静态存储期(Static Storage Duration):全局变量、静态变量
  • 动态存储期(Dynamic Storage Duration):堆上分配的对象
  • 线程存储期(Thread Storage Duration):thread_local变量

1. 自动存储期

自动存储期对象在进入作用域时构造,离开作用域时析构,遵循RAII原则。

#include <iostream>
 
class LifetimeDemo {
public:
    int value;
    
    LifetimeDemo(int v) : value(v) {
        std::cout << "LifetimeDemo 构造: " << value << std::endl;
    }
    
    ~LifetimeDemo() {
        std::cout << "LifetimeDemo 析构: " << value << std::endl;
    }
    
    LifetimeDemo(const LifetimeDemo& other) : value(other.value) {
        std::cout << "LifetimeDemo 拷贝构造: " << value << std::endl;
    }
};
 
void demonstrateAutomaticLifetime() {
    std::cout << "=== 自动存储期演示 ===" << std::endl;
    
    std::cout << "进入函数" << std::endl;
    
    LifetimeDemo obj1(1);  // 自动变量
    
    {
        std::cout << "进入内层块" << std::endl;
        LifetimeDemo obj2(2);  // 块作用域自动变量
        
        {
            std::cout << "进入最内层块" << std::endl;
            LifetimeDemo obj3(3);
            std::cout << "离开最内层块" << std::endl;
        }  // obj3 在这里析构
        
        std::cout << "离开内层块" << std::endl;
    }  // obj2 在这里析构
    
    std::cout << "离开函数" << std::endl;
}  // obj1 在这里析构
 
void demonstrateTemporaryLifetime() {
    std::cout << "\n=== 临时对象生命周期 ===" << std::endl;
    
    // 临时对象的生命周期
    std::cout << "创建临时对象:" << std::endl;
    LifetimeDemo temp = LifetimeDemo(10);  // 可能的优化:直接构造
    
    // 表达式中的临时对象
    std::cout << "\n表达式中的临时对象:" << std::endl;
    int result = LifetimeDemo(20).value + LifetimeDemo(30).value;
    std::cout << "表达式结果: " << result << std::endl;
    
    // 引用延长临时对象生命周期
    std::cout << "\n引用延长生命周期:" << std::endl;
    const LifetimeDemo& ref = LifetimeDemo(40);
    std::cout << "引用的值: " << ref.value << std::endl;
    std::cout << "函数结束前" << std::endl;
}  // ref 引用的临时对象在这里析构
 
// 返回值优化演示
LifetimeDemo createObject(int value) {
    std::cout << "在函数中创建对象" << std::endl;
    return LifetimeDemo(value);  // RVO优化
}

2. 静态存储期

静态存储期对象在程序启动时创建,程序结束时销毁。包括全局变量、静态局部变量、静态成员变量。

#include <iostream>
 
class StaticDemo {
public:
    int value;
    
    StaticDemo(int v) : value(v) {
        std::cout << "StaticDemo 构造: " << value << std::endl;
    }
    
    ~StaticDemo() {
        std::cout << "StaticDemo 析构: " << value << std::endl;
    }
};
 
// 全局对象 - 静态存储期
StaticDemo globalObj(100);
 
void demonstrateStaticLifetime() {
    std::cout << "=== 静态存储期演示 ===" << std::endl;
    
    // 局部静态变量 - 第一次调用时初始化
    static StaticDemo localStatic(200);
    static int counter = 0;
    
    ++counter;
    std::cout << "函数调用次数: " << counter << std::endl;
    std::cout << "局部静态对象值: " << localStatic.value << std::endl;
    
    // 修改静态变量
    localStatic.value += 10;
}
 
class ClassWithStatic {
public:
    static StaticDemo staticMember;
    static int staticValue;
    
    static void accessStaticMember() {
        std::cout << "静态成员值: " << staticMember.value << std::endl;
    }
};
 
// 静态成员定义 - 必须在类外定义
StaticDemo ClassWithStatic::staticMember(300);
int ClassWithStatic::staticValue = 42;
 
// 单例模式 - 利用函数静态变量的延迟初始化
StaticDemo& getSingleton() {
    static StaticDemo instance(400);  // 第一次调用时初始化,线程安全(C++11)
    return instance;
}
 
// 静态初始化顺序问题演示
StaticDemo earlyGlobal(50);   // 可能在其他全局对象之前初始化

3. 动态存储期

动态存储期对象在堆上分配,需要显式管理其生命周期。现代C++推荐使用智能指针。

#include <iostream>
#include <memory>
#include <vector>
 
class DynamicDemo {
public:
    int value;
    
    DynamicDemo(int v) : value(v) {
        std::cout << "DynamicDemo 构造: " << value << std::endl;
    }
    
    ~DynamicDemo() {
        std::cout << "DynamicDemo 析构: " << value << std::endl;
    }
    
    // 移动构造函数
    DynamicDemo(DynamicDemo&& other) noexcept : value(other.value) {
        std::cout << "DynamicDemo 移动构造: " << value << std::endl;
        other.value = 0;
    }
};
 
void demonstrateDynamicLifetime() {
    std::cout << "=== 动态存储期演示 ===" << std::endl;
    
    // 1. 原始指针 - 手动管理(不推荐)
    std::cout << "原始指针管理:" << std::endl;
    DynamicDemo* rawPtr = new DynamicDemo(500);
    std::cout << "使用动态对象: " << rawPtr->value << std::endl;
    delete rawPtr;  // 手动删除,容易忘记或出错
    rawPtr = nullptr;
    
    // 2. unique_ptr - 独占所有权
    std::cout << "\nunique_ptr 管理:" << std::endl;
    {
        std::unique_ptr<DynamicDemo> uniquePtr = std::make_unique<DynamicDemo>(600);
        std::cout << "使用 unique_ptr: " << uniquePtr->value << std::endl;
        
        // 转移所有权
        std::unique_ptr<DynamicDemo> anotherPtr = std::move(uniquePtr);
        if (!uniquePtr) {
            std::cout << "原 unique_ptr 已无效" << std::endl;
        }
        std::cout << "新 unique_ptr 值: " << anotherPtr->value << std::endl;
    }  // anotherPtr 超出作用域,自动删除对象
    
    // 3. shared_ptr - 引用计数管理
    std::cout << "\nshared_ptr 管理:" << std::endl;
    {
        std::shared_ptr<DynamicDemo> sharedPtr1 = std::make_shared<DynamicDemo>(700);
        std::cout << "引用计数: " << sharedPtr1.use_count() << std::endl;
        
        {
            std::shared_ptr<DynamicDemo> sharedPtr2 = sharedPtr1;  // 共享所有权
            std::cout << "共享后引用计数: " << sharedPtr1.use_count() << std::endl;
            std::cout << "两个指针指向同一对象: " << (sharedPtr1.get() == sharedPtr2.get()) << std::endl;
        }  // sharedPtr2 析构,引用计数减1
        
        std::cout << "内层作用域结束后引用计数: " << sharedPtr1.use_count() << std::endl;
    }  // sharedPtr1 析构,引用计数为0,对象被删除
    
    // 4. weak_ptr - 打破循环引用
    std::cout << "\nweak_ptr 打破循环引用:" << std::endl;
    {
        std::shared_ptr<DynamicDemo> shared = std::make_shared<DynamicDemo>(800);
        std::weak_ptr<DynamicDemo> weak = shared;
        
        std::cout << "shared 引用计数: " << shared.use_count() << std::endl;
        std::cout << "weak 是否过期: " << weak.expired() << std::endl;
        
        if (auto locked = weak.lock()) {  // 尝试获取 shared_ptr
            std::cout << "通过 weak_ptr 访问对象: " << locked->value << std::endl;
        }
        
        shared.reset();  // 释放 shared_ptr
        std::cout << "shared.reset() 后 weak 是否过期: " << weak.expired() << std::endl;
    }
    
    // 5. 数组的动态分配
    std::cout << "\n动态数组管理:" << std::endl;
    {
        // 使用 unique_ptr 管理数组
        std::unique_ptr<DynamicDemo[]> arrayPtr(new DynamicDemo[3]{901, 902, 903});
        for (int i = 0; i < 3; ++i) {
            std::cout << "数组元素[" << i << "]: " << arrayPtr[i].value << std::endl;
        }
    }  // 数组自动释放
    
    // 6. 自定义删除器
    std::cout << "\n自定义删除器:" << std::endl;
    {
        auto customDeleter = [](DynamicDemo* p) {
            std::cout << "使用自定义删除器删除对象: " << p->value << std::endl;
            delete p;
        };
        
        std::unique_ptr<DynamicDemo, decltype(customDeleter)> 
            customPtr(new DynamicDemo(999), customDeleter);
    }
}
 
// 内存泄漏演示(仅用于教学,实际代码中避免)
void memoryLeakDemo() {
    std::cout << "\n=== 内存泄漏演示 ===" << std::endl;
    
    // 错误:忘记释放内存
    DynamicDemo* leaked = new DynamicDemo(1000);
    // 没有 delete leaked; - 这会导致内存泄漏
    
    std::cout << "对象被创建但永远不会被删除(内存泄漏)" << std::endl;
}

4. 线程存储期

线程存储期对象在线程开始时创建,线程结束时销毁。

#include <iostream>
#include <thread>
#include <vector>
 
class ThreadDemo {
public:
    int value;
    
    ThreadDemo(int v) : value(v) {
        std::cout << "ThreadDemo 构造: " << value 
                  << " (线程ID: " << std::this_thread::get_id() << ")" << std::endl;
    }
    
    ~ThreadDemo() {
        std::cout << "ThreadDemo 析构: " << value 
                  << " (线程ID: " << std::this_thread::get_id() << ")" << std::endl;
    }
};
 
// thread_local 变量
thread_local ThreadDemo tlsObj(2000);
 
void threadFunction(int id) {
    std::cout << "\n线程 " << id << " 开始执行" << std::endl;
    
    // 访问 thread_local 变量
    tlsObj.value += id;
    std::cout << "线程 " << id << " 中的 tlsObj 值: " << tlsObj.value << std::endl;
    
    // 局部 thread_local 变量
    thread_local ThreadDemo localTLS(3000 + id);
    localTLS.value += 100;
    std::cout << "线程 " << id << " 中的局部 tlsObj 值: " << localTLS.value << std::endl;
    
    std::cout << "线程 " << id << " 结束执行" << std::endl;
}
 
void demonstrateThreadLifetime() {
    std::cout << "=== 线程存储期演示 ===" << std::endl;
    
    std::vector<std::thread> threads;
    
    // 创建多个线程
    for (int i = 1; i <= 3; ++i) {
        threads.emplace_back(threadFunction, i);
    }
    
    // 等待所有线程完成
    for (auto& t : threads) {
        t.join();
    }
    
    std::cout << "\n所有线程执行完毕" << std::endl;
}

5. 主函数和测试

// 完整的测试主函数
int main() {
    std::cout << "===== C++ 对象生命周期演示开始 =====" << std::endl;
    
    // 1. 自动存储期
    demonstrateAutomaticLifetime();
    demonstrateTemporaryLifetime();
    
    // RVO演示
    std::cout << "\n=== 返回值优化演示 ===" << std::endl;
    auto obj = createObject(50);
    std::cout << "返回的对象值: " << obj.value << std::endl;
    
    // 2. 静态存储期
    std::cout << "\nmain 开始执行" << std::endl;
    
    // 多次调用函数,观察静态变量行为
    for (int i = 0; i < 3; ++i) {
        std::cout << "\n第 " << (i + 1) << " 次调用:" << std::endl;
        demonstrateStaticLifetime();
    }
    
    // 访问类静态成员
    std::cout << "\n访问类静态成员:" << std::endl;
    ClassWithStatic::accessStaticMember();
    
    // 访问单例
    std::cout << "\n访问单例:" << std::endl;
    StaticDemo& singleton1 = getSingleton();
    StaticDemo& singleton2 = getSingleton();
    std::cout << "单例地址相同: " << (&singleton1 == &singleton2) << std::endl;
    
    // 3. 动态存储期
    demonstrateDynamicLifetime();
    
    // 4. 线程存储期
    demonstrateThreadLifetime();
    
    // 5. 内存泄漏演示(教学用)
    memoryLeakDemo();
    
    std::cout << "\n===== main 函数结束 =====" << std::endl;
    return 0;
}
// 程序结束时,静态对象按构造的逆序析构

关键要点总结

生命周期管理最佳实践

  1. 优先使用自动存储期:栈上对象自动管理,性能好,异常安全
  2. 避免原始指针:使用智能指针管理动态内存
  3. 理解移动语义:减少不必要的拷贝,提高性能
  4. 注意静态初始化顺序:全局静态对象的初始化顺序不确定
  5. 使用RAII原则:资源获取即初始化,确保资源正确释放

常见陷阱

  • 悬空指针:指向已销毁对象的指针
  • 内存泄漏:动态分配的对象未正确释放
  • 静态初始化顺序问题:全局对象依赖关系导致的问题
  • 循环引用:shared_ptr 循环引用导致内存泄漏

性能考虑

  • 自动存储期 > 静态存储期 > 动态存储期(一般情况)
  • 栈分配比堆分配快
  • 智能指针有轻微性能开销,但安全性收益巨大