1441 字
7 分钟
智能指针
2025-03-12

C++中的智能指针#

智能指针是C++中用于管理动态内存的一种机制,它封装了原始指针,提供了自动的内存管理功能,从而帮助程序员避免内存泄漏、指针越界等常见问题。C++11引入了三种主要的智能指针类型:unique_ptrshared_ptrweak_ptr

1. unique_ptr#

unique_ptr是独占式的智能指针,它确保同一时间只有一个智能指针可以指向某个动态分配的对象。当unique_ptr被销毁时,它所管理的对象也会被自动销毁。 基本用法:

#include <memory>
int main() {
    std::unique_ptr<int> ptr1(new int(10));  // 创建一个unique_ptr
    // std::unique_ptr<int> ptr2 = ptr1;  // 错误,不能复制unique_ptr
    std::unique_ptr<int> ptr3 = std::move(ptr1);  // 转移所有权
    if (ptr1 == nullptr) {
        // 此时ptr1不再拥有对象
    }
    // 使用ptr3
    *ptr3 = 20;
    return 0;  // ptr3自动释放内存
}

特点:

  • 独占性:不能共享所管理的对象,不能进行复制操作,但可以转移所有权。
  • 自动释放:当unique_ptr离开作用域时,自动释放所管理的对象。
  • 高效:由于独占性,不需要引用计数,效率较高。

2. shared_ptr#

shared_ptr是共享式的智能指针,允许多个智能指针共享同一个动态分配的对象。它通过引用计数来管理对象的生命周期,当引用计数为0时,对象被自动销毁。 基本用法:

#include <memory>
int main() {
    std::shared_ptr<int> ptr1(new int(10));  // 创建一个shared_ptr
    std::shared_ptr<int> ptr2 = ptr1;  // 复制shared_ptr,引用计数+1
    {
        std::shared_ptr<int> ptr3 = ptr1;  // 引用计数+1
        // 使用ptr3
    }  // ptr3离开作用域,引用计数-1
    // 使用ptr1和ptr2
    *ptr1 = 20;
    return 0;  // ptr1和ptr2离开作用域,引用计数为0,对象被销毁
}

特点:

  • 共享性:多个shared_ptr可以共享同一个对象。
  • 引用计数:内部维护一个引用计数,用于管理对象的生命周期。
  • 自动释放:当引用计数为0时,自动释放对象。

3. weak_ptr#

weak_ptr是一种弱引用智能指针,它不增加引用计数,主要用于解决shared_ptr可能引起的循环引用问题。 基本用法:

#include <memory>
int main() {
    std::shared_ptr<int> ptr1(new int(10));
    std::weak_ptr<int> weakPtr = ptr1;  // 创建一个weak_ptr
    if (std::shared_ptr<int> sharedPtr = weakPtr.lock()) {  // 尝试提升为shared_ptr
        // 对象存在,可以使用sharedPtr
    } else {
        // 对象已被销毁
    }
    return 0;  // ptr1离开作用域,对象被销毁,weakPtr变为空
}

特点:

  • 弱引用:不增加引用计数,不会影响对象的生命周期。
  • 提升:可以通过lock()方法尝试提升为shared_ptr,如果对象存在,则提升成功,否则返回空shared_ptr
  • 解决循环引用:常用于解决shared_ptr之间的循环引用问题。

智能指针的优势#

  • 自动管理内存:减少手动管理内存的需求,降低内存泄漏和指针错误的风险。
  • 提高代码安全性:通过封装原始指针,提供更安全的操作方式。
  • 简化代码:减少内存管理相关的代码,使代码更简洁易读。

注意事项#

  • 避免与原始指针混用:智能指针和原始指针混用可能导致未定义行为。
  • 正确使用newdelete:使用智能指针后,应避免手动调用newdelete
  • 理解循环引用:在使用shared_ptr时,注意循环引用问题,必要时使用weak_ptr。 智能指针是C++现代编程中非常重要的特性,合理使用智能指针可以大大提高代码的健壮性和可维护性。

使用std::unique_ptr#

场景:当你想要确保只有一个指针拥有对动态分配对象的访问权时。 示例

#include <iostream>
#include <memory>
class Resource {
public:
    void use() {
        std::cout << "Using resource" << std::endl;
    }
    ~Resource() {
        std::cout << "Resource destroyed" << std::endl;
    }
};
int main() {
    // 创建一个unique_ptr管理Resource对象
    std::unique_ptr<Resource> resPtr(new Resource());
    // 使用资源
    resPtr->use();
    // 不需要手动删除资源,resPtr离开作用域时会自动删除
    return 0;
}

使用技巧

  • 使用std::make_unique(C++14起可用)来创建unique_ptr,避免内存分配和构造函数调用之间的异常安全问题。
    • auto resPtr = std::make_unique<Resource>();
  • 在函数中返回unique_ptr,以转移所有权。
  • 在容器中存储unique_ptr,以管理动态数组。

使用std::shared_ptr#

场景:当你需要多个指针共享对同一个动态分配对象的访问权时。 示例

#include <iostream>
#include <memory>
class Resource {
public:
    void use() {
        std::cout << "Using resource" << std::endl;
    }
    ~Resource() {
        std::cout << "Resource destroyed" << std::endl;
    }
};
int main() {
    // 创建shared_ptr
    std::shared_ptr<Resource> sharedRes = std::make_shared<Resource>();
    // 复制shared_ptr
    std::shared_ptr<Resource> anotherSharedRes = sharedRes;
    // 使用资源
    sharedRes->use();
    anotherSharedRes->use();
    // 当所有shared_ptr都离开作用域时,资源会被自动删除
    return 0;
}

使用技巧

  • 使用std::make_shared来创建shared_ptr,以提高效率。
  • 注意避免循环引用,这会导致内存泄漏。
  • 在需要共享资源的多线程环境中使用shared_ptr

使用std::weak_ptr#

场景:用于解决std::shared_ptr的循环引用问题,或者当你需要观察某个对象是否存在而不影响其生命周期时。 示例

#include <iostream>
#include <memory>
class Resource {
public:
    void use() {
        std::cout << "Using resource" << std::endl;
    }
    ~Resource() {
        std::cout << "Resource destroyed" << std::endl;
    }
};
int main() {
    std::shared_ptr<Resource> sharedRes = std::make_shared<Resource>();
    std::weak_ptr<Resource> weakRes = sharedRes;
    // 尝试提升weak_ptr到shared_ptr
    if (std::shared_ptr<Resource> tempRes = weakRes.lock()) {
        tempRes->use();
    } else {
        std::cout << "Resource is no longer available" << std::endl;
    }
    // 销毁shared_ptr
    sharedRes.reset();
    // 再次尝试提升
    if (weakRes.expired()) {
        std::cout << "Resource has been destroyed" << std::endl;
    }
    return 0;
}

使用技巧

  • 使用weak_ptr来打破shared_ptr的循环引用。
  • 在需要观察对象是否存在时,使用weak_ptr而不影响对象的生命周期。
  • 使用weak_ptr::lock来安全地访问对象,如果对象已被销毁,则返回一个空的shared_ptr
智能指针
http://zaoweiceng.github.io/posts/cpp/智能指针/
作者
静惜
发布于
2025-03-12
许可协议
CC BY-NC-SA 4.0