C++ 中的对象生命周期(堆栈/作用域生命周期)

在 C++ 中,对象的生命周期是指对象在程序中存在的时间段,包括对象的创建、使用和销毁。理解对象生命周期对于管理内存和资源非常重要。对象的生命周期主要可以分为两种:栈(Stack)生命周期和堆(Heap)生命周期。下面将详细解释这两种生命周期以及它们的特点。

1. 栈生命周期

栈生命周期的对象是自动管理的,也就是说它们在栈上分配内存,通常在函数的作用域内创建并销毁。

1.1 创建与销毁
  • 创建:当程序执行到对象的定义语句时,栈上的对象被创建并分配内存。
  • 销毁:当对象的作用域结束时,栈上的对象自动被销毁,内存被释放。
1.2 作用域

对象的作用域是指对象可被访问的区域。在 C++ 中,栈对象的作用域通常是局部的,例如在一个函数内或一个代码块内。栈对象在离开作用域时会自动销毁。

1.4 特点
  • 快速分配和销毁:栈上的对象在创建和销毁时速度很快,因为只需移动栈指针。
  • 自动管理:栈上的对象的生命周期由作用域自动管理,避免了内存泄漏的问题。
  • 有限的大小:栈的大小是有限的,因此栈上的对象不能过大,否则可能导致栈溢出(stack overflow)。

2. 堆生命周期

堆生命周期的对象是动态管理的,内存是通过动态分配来管理的。堆上的对象在创建时必须显式管理它们的生命周期。

2.1 创建与销毁
  • 创建:使用 new 操作符创建对象时,内存分配在堆上。
  • 销毁:堆上的对象必须显式调用 delete 操作符来销毁,以释放内存。不使用delete来删除的话,可能会导致内存泄漏,所以在创建类指针时,必须在程序使用结束后使用delete来删除,不然很有kenne
2.3 特点
  • 灵活性:堆上的对象可以在程序运行时动态创建,生命周期不受作用域的限制。
  • 手动管理:开发者必须手动管理堆内存,确保在使用完后调用 delete,否则会导致内存泄漏。
  • 内存碎片:由于内存分配和释放的随机性,长时间使用可能会导致内存碎片化,影响程序性能。

3. 作用域和生命周期的关系

  • 栈对象的生命周期与作用域直接相关:当对象离开其作用域时,它会被自动销毁。
  • 堆对象的生命周期与作用域无关:即使在超出作用域的情况下,只要不调用 delete,对象仍然存在。

                                         自定义智能指针与匿名作用域

#include 
class Entity
{
    public:
    int data;
    Entity(int data) : data(data){
        std::cout<<"Entity created"<

ScopedPtr 是一个智能指针类,旨在管理动态分配的 Entity 对象的生命周期。

ScopedPtr 对象被销毁时,它的析构函数会自动调用 delete m_ptr,以释放 ScopedPtr 所管理的 Entity 对象的内存。这可以避免内存泄漏。

 

匿名作用域

main 函数中,创建了一个匿名作用域(用 {} 包围的部分)。这意味着该作用域内的所有对象将在退出该作用域时被销毁。

当离开匿名作用域时,ScopedPtr 对象 sp 被销毁,其析构函数被调用,进而调用 delete m_ptr 释放与之关联的 Entity(1) 对象的内存。这会输出 "Entity destroyed"

                                                     C++中的智能指针  

C++中的智能指针(Smart Pointers)是通过RAII(资源获取即初始化)机制来自动管理动态内存资源的对象。与传统的裸指针不同,智能指针会在不再使用时自动释放所管理的资源,从而避免内存溢出问题。C++标准库中提供了清晰常见的智能指针类型:

1. std::unique_ptr

特点:
  • 独占所有权std::unique_ptr 拥有所指向对象的独占所有权。即只有一个 std::unique_ptr 可以指向某个对象。
  • 不可复制:不能复制 std::unique_ptr,但可以通过 移动语义 (std::move()) 将所有权从一个 std::unique_ptr 转移到另一个。
  • 自动析构:当 std::unique_ptr 超出作用域时,自动调用 delete 释放对象。

2. std::shared_ptr

特点:
  • 共享所有权:多个 std::shared_ptr 可以共享同一对象的所有权,使用 引用计数 来跟踪有多少个 shared_ptr 指向同一个对象。
  • 自动释放:当最后一个 std::shared_ptr 被销毁时(即引用计数降为 0),对象会被自动销毁。
  • 开销:由于维护引用计数,std::shared_ptr 的性能开销比 std::unique_ptr 稍高。

C++ 中的智能指针是一种自动管理动态内存的工具,它帮助开发者避免手动管理内存释放和潜在的内存泄漏问题。智能指针的核心思想是利用 RAII(Resource Acquisition Is Initialization)机制,在对象生命周期结束时自动释放资源。C++ 提供了三种主要类型的智能指针:std::unique_ptrstd::shared_ptrstd::weak_ptr,它们都位于 头文件中。

1. std::unique_ptr

特点:
  • 独占所有权std::unique_ptr 拥有所指向对象的独占所有权。即只有一个 std::unique_ptr 可以指向某个对象。
  • 不可复制:不能复制 std::unique_ptr,但可以通过 移动语义 (std::move()) 将所有权从一个 std::unique_ptr 转移到另一个。
  • 自动析构:当 std::unique_ptr 超出作用域时,自动调用 delete 释放对象。
#include 
#include 

class MyClass {
public:
    MyClass() { std::cout << "Constructor\n"; }
    ~MyClass() { std::cout << "Destructor\n"; }
};

int main() {
    std::unique_ptr ptr1 = std::make_unique();  // 创建对象并将其所有权赋给 ptr1

    // 转移所有权
    std::unique_ptr ptr2 = std::move(ptr1);  // ptr1 不再拥有对象,ptr2 拥有对象

    // 离开作用域时,ptr2 会自动释放 MyClass 对象
    return 0;
}

2. std::shared_ptr

特点:
  • 共享所有权:多个 std::shared_ptr 可以共享同一对象的所有权,使用 引用计数 来跟踪有多少个 shared_ptr 指向同一个对象。
  • 自动释放:当最后一个 std::shared_ptr 被销毁时(即引用计数降为 0),对象会被自动销毁。
  • 开销:由于维护引用计数,std::shared_ptr 的性能开销比 std::unique_ptr 稍高。
#include 
#include 

class MyClass {
public:
    MyClass() { std::cout << "Constructor\n"; }
    ~MyClass() { std::cout << "Destructor\n"; }
};

int main() {
    std::shared_ptr ptr1 = std::make_shared();  // 创建共享指针

    {
        std::shared_ptr ptr2 = ptr1;  // ptr1 和 ptr2 共享同一对象
        std::cout << "Shared count: " << ptr1.use_count() << "\n";  // 输出 2
    }  // ptr2 离开作用域,引用计数降为 1

    std::cout << "Shared count: " << ptr1.use_count() << "\n";  // 输出 1
    // ptr1 离开作用域,引用计数降为 0,MyClass 对象被释放

    return 0;
}

3. std::weak_ptr

特点:
  • 弱引用std::weak_ptr 并不拥有对象的所有权,它指向一个由 std::shared_ptr 管理的对象,但不会影响引用计数。
  • 防止循环引用std::weak_ptr 通常与 std::shared_ptr 配合使用,防止两个对象相互引用,造成内存泄漏。
  • 手动检查有效性:使用 std::weak_ptr 时,需要调用 lock() 方法获取一个有效的 std::shared_ptr,并检查其是否仍然有效。
#include 
#include 

class MyClass {
public:
    MyClass() { std::cout << "Constructor\n"; }
    ~MyClass() { std::cout << "Destructor\n"; }
};

int main() {
    std::shared_ptr ptr1 = std::make_shared();  // 创建共享指针
    std::weak_ptr weakPtr = ptr1;  // 创建一个弱引用,指向同一对象

    // 检查对象是否仍然存在
    if (auto sharedPtr = weakPtr.lock()) {
        std::cout << "Object is still alive\n";
    } else {
        std::cout << "Object has been destroyed\n";
    }

    return 0;
}

适用场景

  • std::unique_ptr:适用于那些不需要共享所有权的场景,如类的成员对象管理等。它是轻量的,性能开销最小。
  • std::shared_ptr:当多个对象或函数需要共享同一个资源时,可以使用 std::shared_ptr,例如管理复杂的数据结构或异步任务。
  • std::weak_ptr:当你需要打破 std::shared_ptr 之间的循环依赖,或者需要观察某个对象的生命周期时,std::weak_ptr 是最佳选择。

智能指针大大减少了内存泄漏和悬空指针的问题,但也需要注意在适当的场景下选择合适的类型,避免性能开销或设计不当导致的复杂性。

#include 
#include 
#include 

class Entity
{
   public:
      Entity()
      {
        std::cout<<"Entity created!"< e(new Entity());
        std::unique_ptr e2 = std::make_unique(); 
        //不可隐式转化,只能显式引用生成std::unique_ptre3 = new Entity();
 

        e->Print();
        e2->Print();
    }
}

 

{

        std::shared_ptr e0;
    {
        //共享指针
        //std::shared_ptr SharedEntity(new Entity());
        std::shared_ptr SharedEntity = std::make_shared();
        e0 = SharedEntity;
        std::weak_ptr WeakPtr = e0;
     
    }
 }

你可能感兴趣的:(java,jvm,前端)