智能指针种类及其使用场景

01.指针管理的困境

  • 资源释放,指针未置空

    • 野指针:未初始化/指向非法地址的指针
    • 指针悬空:多个指针指向同一资源
    • 踩内存:多次释放
  • 资源未释放:导致内存泄漏

  • 资源未释放,引发coresump


02.怎么解决?

智能指针通过RAII模式,确保资源在对象生命周期结束时自动释放,从根本上解决了手动内存管理的各种问题。

智能指针资源管理
├── 问题类型
│   ├── 资源释放,指针未置空 → unique_ptr/shared_ptr自动管理
│   ├── 野指针问题 → 智能指针初始化保证有效性
│   ├── 指针悬空 → weak_ptr解决循环引用
│   ├── 踩内存 → RAII机制防止越界
│   └── 资源未释放 → 自动析构释放资源
└── 解决方案
    ├── unique_ptr - 独占所有权
    ├── shared_ptr - 共享所有权
    └── weak_ptr - 观察而不拥有

03.智能指针的种类

3.1 unique_ptr

独享所有权,辅助shared_ptr,用来解决shared_ptr循环引用,原因是弱引用不占用强引用计数没有。不能拷贝,只能移动,离开作用域自动释放内存。

在这里插入图片描述

class Resource
{
public:
    Resource() { cout << "Resource" << endl; }
    void doWork()
    { // 更直观的方法名
        cout << "Resource is working" << endl;
    }

    ~Resource() { cout << "~Resource" << endl; }
};

int main()
{
    // 方式1,1:使用make_unique(推荐)C++14
    std::unique_ptr<Resource> ptr0 = std::make_unique<Resource>();
    ptr0->doWork();
	// 方式1,2:使用new
    unique_ptr<Resource> ptr1(new Resource());
    ptr1->doWork();

    // 方式2:演示所有权转移
    unique_ptr<Resource> ptr2 = move(ptr1);
    if (!ptr1)
    {
        cout << "ptr1 is now empty" << endl;
    }
    ptr2->doWork();
    return 0;
}

3.2 shared_ptr

std::shared_ptr 是一种共享所有权的智能指针,多个 shared_ptr 可以指向同一个对象,资源没有明确的拥有者,通过引用计数(原子op)机制来管理对象的生命周期。当最后一个shared_ptr被销毁时,对象自动删除。通常应用在容器中管理指针/资源通过函数传递。不要使用裸指针(直接使用*声明的指针,eg:Resoure *ptr = new Resoure(); )而使用make_shared

void containerUsage() {
    cout << "\n=== 容器中使用 shared_ptr ===" << endl;
    
    vector<shared_ptr<Resource>> resources;
    
    // 向容器添加共享指针
    resources.push_back(make_shared<Resource>("Resource1"));
    resources.push_back(make_shared<Resource>("Resource2"));
    resources.push_back(resources[0]);  // 共享第一个资源
    
    cout << "容器大小: " << resources.size() << endl;
    cout << "Resource1 引用计数: " << resources[0].use_count() << endl;
    
    for (const auto& resource : resources) {
        resource->doWork();
    }
    
    // 清空容器
    resources.clear();
    cout << "容器清空后" << endl;
}

循环引用导致内存泄漏。

class BadNode {
public:
    shared_ptr<BadNode> next;
    shared_ptr<BadNode> prev;
    string name;
    
    BadNode(string name) : name(name) {
        cout << "BadNode " << endl;
    }
   
    ~BadNode() {
        cout << " ~BadNode" << endl;
    }
};

int() { 
    auto node1 = make_shared<BadNode>("Node1");
    auto node2 = make_shared<BadNode>("Node2");
    
    node1->next = node2;  // node2 引用计数: 2
    node2->prev = node1;  // node1 引用计数: 2
    
    cout << "Node1 引用计数: " << node1.use_count() << endl;
    cout << "Node2 引用计数: " << node2.use_count() << endl;
    
    // 离开作用域时,引用计数减为1,对象不会被销毁!
    return 0;
}

那么接下来使用weak_ptr即可解决。


3.3 weak_ptr

辅助shared_ptr,用来解决shared_ptr循环引用,原因是弱引用不占用强引用计数。

class GoodNode {
public:
    shared_ptr<GoodNode> next;
    weak_ptr<GoodNode> prev;  // 使用 weak_ptr 打破一个方向的循环
    string name;
    
    GoodNode(string name) : name(name) {
        cout << "GoodNode '" << name << "' created" << endl;
    }
    
    ~GoodNode() {
        cout << "GoodNode '" << name << "' destroyed" << endl;
    }
};

void circularReferenceSolution() {  
    auto node1 = make_shared<GoodNode>("Node1");
    auto node2 = make_shared<GoodNode>("Node2");
    
    node1->next = node2;
    node2->prev = node1;  // 使用 weak_ptr,不会增加引用计数
    
    cout << "Node1 引用计数: " << node1.use_count() << endl;  // 1
    cout << "Node2 引用计数: " << node2.use_count() << endl;  // 2(node1->next 持有)
    
    // 离开作用域时,对象会被正确销毁
}

ut << "Node1 引用计数: " << node1.use_count() << endl; // 1
cout << "Node2 引用计数: " << node2.use_count() << endl; // 2(node1->next 持有)

// 离开作用域时,对象会被正确销毁

}


Logo

有“AI”的1024 = 2048,欢迎大家加入2048 AI社区

更多推荐