C++智能指针

一、普通指针的缺点

  • 普通指针需要手动释放内存
  • 相互引用时内存无法释放

二、智能指针

1、智能指针介绍

  • 智能指针本质是对象: 它们封装了原始指针,并提供了自动内存管理功能。
  • 构造函数和析构函数: 智能指针具有构造函数和析构函数,这些构造函数和析构函数在智能指针的生命周期内管理资源的分配和释放。例如,shared_ptr 会在对象被销毁时自动减少引用计数,并在引用计数为零时释放资源。
  • 成员函数 : 智能指针提供了一些成员函数,用于访问和管理其封装的指针。例如,unique_ptr 提供 releaseresetget 等成员函数,shared_ptr 提供 use_countuniquereset 等成员函数。
  • 运算符重载 : 智能指针重载了一些运算符,使其使用起来更像原始指针。例如,operator*operator-> 允许智能指针像原始指针一样使用。
cpp 复制代码
#include <iostream>
#include <memory>
 
int main() 
{
    std::unique_ptr<int> uniquePtr = std::make_unique<int>(10); // unique_ptr 是一个对象
    std::shared_ptr<int> sharedPtr = std::make_shared<int>(20); // shared_ptr 是一个对象
    std::weak_ptr<int> weakPtr = sharedPtr;                     // weak_ptr 是一个对象
 
    std::cout << "uniquePtr: " << *uniquePtr << std::endl;
    std::cout << "sharedPtr: " << *sharedPtr << std::endl;
 
    // 调用 shared_ptr 的成员函数 use_count()
    std::cout << "sharedPtr use count: " << sharedPtr.use_count() << std::endl;
 
    return 0;
}

2、智能指针分类

  • std::unique_ptr: 提供独占所有权的智能指针,适用于单一所有权场景。
  • std::shared_ptr: 提供共享所有权的智能指针,适用于多个对象共享同一资源的场景。
  • std::weak_ptr: 用于解决**shared_ptr**的循环引用问题,不参与引用计数。

3、std::unique_ptr 介绍

  • 创建一个std::unique_ptr
cpp 复制代码
//使用std::make_unique
auto ptr = std::make_unique<int>(10);

//使用构造函数
std::unique_ptr<int> ptr(new int(10));
  • std::unique_ptr不可复制和拷贝构造,只能使用std::move转移所有权
cpp 复制代码
#include <iostream>
#include <memory>
 
int main() 
{
    auto ptr1 = std::make_unique<int>(10); // 创建一个指向整数 10 的 unique_ptr
    std::unique_ptr<int> ptr2 = std::move(ptr1); // 转移所有权给 ptr2
    if (!ptr1) {
        std::cout << "ptr1 is null" << std::endl; // ptr1 现在为空
    }
    std::cout << "Value: " << *ptr2 << std::endl; // 输出 10
    return 0; // ptr2 超出作用域时自动释放内存
}
  • 使用实例
cpp 复制代码
#include <iostream> 
#include <memory> 
int main() 
{ 
    // 创建一个 std::unique_ptr,拥有整数对象的唯一所有权 
    std::unique_ptr<int> uniqueInt = std::make_unique<int>(42); 

    // 唯一所有权转移 
    std::unique_ptr<int> anotherUniqueInt = std::move(uniqueInt); 

    // 访问唯一的对象 int value = *anotherUniqueInt; 
    std::cout << "anotherUniqueInt: " << value << std::endl; 

    // 释放 anotherUniqueInt 指向的资源 anotherUniqueInt.reset(); 

    // 检查是否为空 
    if (!anotherUniqueInt) 
    { 
        std::cout << "anotherUniqueInt is null" << std::endl; 
    } 
    return 0; 
}

4、std::shared_ptr介绍

  • 创建 shared_ptr
cpp 复制代码
#include <iostream>
#include <memory>
 
int main() 
{
    // 使用 make_shared 创建 shared_ptr
    std::shared_ptr<int> ptr1 = std::make_shared<int>(10);
    std::cout << "Value: " << *ptr1 << std::endl; // 输出 10
 
    // 使用构造函数创建 shared_ptr
    std::shared_ptr<int> ptr2(new int(20));
    std::cout << "Value: " << *ptr2 << std::endl; // 输出 20
 
    return 0;
}
  • 共享所有权
    多个 shared_ptr 可以指向同一个对象,并共享该对象的所有权:
cpp 复制代码
#include <iostream>
#include <memory>
 
int main() 
{
    std::shared_ptr<int> ptr1 = std::make_shared<int>(30);
 
    // ptr2 共享 ptr1 所指向的对象
    std::shared_ptr<int> ptr2 = ptr1;
 
    std::cout << "Value from ptr1: " << *ptr1 << std::endl; // 输出 30
    std::cout << "Value from ptr2: " << *ptr2 << std::endl; // 输出 30
 
    // 引用计数
    std::cout << "Use count: " << ptr1.use_count() << std::endl; // 输出 2
 
    return 0;
}
  • 避免循环引用
    shared_ptr 可能导致循环引用,从而造成内存泄漏。使用 std::weak_ptr 可以打破循环引用:
cpp 复制代码
#include <iostream>
#include <memory>
 
struct Node 
{
    std::shared_ptr<Node> next;
    std::weak_ptr<Node> prev; // 使用 weak_ptr 打破循环引用
    ~Node() {
        std::cout << "Node destroyed" << std::endl;
    }
};
 
int main() 
{
    auto node1 = std::make_shared<Node>();
    auto node2 = std::make_shared<Node>();
    
    node1->next = node2;
    node2->prev = node1;
 
    std::cout << "Use count of node1: " << node1.use_count() << std::endl; // 输出 1
    std::cout << "Use count of node2: " << node2.use_count() << std::endl; // 输出 1
 
    return 0; // node1 和 node2 超出作用域,内存被释放
}
  • 使用实例
cpp 复制代码
#include <iostream> 
#include <memory> 
int main() { 
    // 创建一个 std::shared_ptr,共享整数对象 
    std::shared_ptr<int> sharedInt = std::make_shared<int>(42); 

    // 共享相同的对象 std::shared_ptr<int> anotherSharedInt = sharedInt; 

    // 访问共享的对象 int value = *sharedInt; 
    int anotherValue = *anotherSharedInt; 
    std::cout << "sharedInt: " << value << std::endl; std::cout << "anotherSharedInt: " << anotherValue << std::endl; 

    // 检查引用计数 int count = sharedInt.use_count(); 
    std::cout << "Reference count: " << count << std::endl; 

    // 释放 sharedInt 指向的资源 sharedInt.reset(); 

    // 再次检查引用计数 count = anotherSharedInt.use_count(); 
    std::cout << "Reference count after reset: " << count << std::endl; 

    return 0; 
}

5、std::weak_ptr介绍

  • weak_ptr创建
    weak_ptr 只能从 shared_ptr 或另一个 weak_ptr 构造,可以使用 lock 方法提升为 shared_ptr ,如果所指向的对象已经被销毁,则 lock() 方法返回空指针。
cpp 复制代码
#include <iostream>
#include <memory>
 
int main() {
    std::shared_ptr<int> sharedPtr = std::make_shared<int>(100);
    std::weak_ptr<int> weakPtr = sharedPtr;
 
    if (auto lockedPtr = weakPtr.lock()) { // 尝试提升为 shared_ptr
        std::cout << "Value: " << *lockedPtr << std::endl; // 输出 100
    } else {
        std::cout << "Pointer is expired" << std::endl;
    }
 
    sharedPtr.reset(); // 手动释放 shared_ptr 所管理的对象
 
    if (weakPtr.expired()) {
        std::cout << "Pointer is expired" << std::endl; // 输出 Pointer is expired
    }
 
    return 0;
}
  • 使用实例
cpp 复制代码
#include <iostream> 
include <memory> 
int main() 
{ 
    // 创建一个 std::shared_ptr 来管理整数对象 
    std::shared_ptr<int> sharedInt = std::make_shared<int>(42); 

    // 创建一个 std::weak_ptr 来观察 sharedInt 
    std::weak_ptr<int> weakInt = sharedInt; 

    // 使用 std::weak_ptr 来访问对象 
    if (auto shared = weakInt.lock()) 
    { 
        int value = *shared; 
        std::cout << "Weak pointer is valid: " << value << std::endl; 
    } 
    else 
    { 
        std::cout << "Weak pointer is expired" << std::endl; 
    } 

    // 释放 sharedInt 的资源 
    sharedInt.reset(); 
    // 再次使用 std::weak_ptr 
    if (auto shared = weakInt.lock()) 
    { 
        std::cout << "Weak pointer is still valid: " << *shared << std::endl; 
    } 
    else 
    { 
        std::cout << "Weak pointer is expired" << std::endl; 
    } 

    return 0; 
}

6、使用场景

在什么情况下选择使用智能指针而不是原始指针?

  • 智能指针适用于需要自动管理生命周期的场景,可以避免内存泄漏和悬挂指针问题。原始指针适用于简单的指针运算和需要精细控制生命周期的场景。
相关推荐
希望_睿智3 小时前
实战设计模式之迭代器模式
c++·设计模式·架构
charlie1145141913 小时前
精读C++20设计模式——行为型设计模式:策略模式
c++·学习·设计模式·策略模式·c++20
郝学胜-神的一滴3 小时前
深入理解 Qt 元对象系统:QMetaEnum 的应用与实践
开发语言·c++·qt·软件工程
艾莉丝努力练剑3 小时前
【C++STL :vector类 (二) 】攻克 C++ Vector 的迭代器失效陷阱:从源码层面详解原理与解决方案
linux·开发语言·c++·经验分享
hope_wisdom3 小时前
C/C++数据结构之用数组实现栈
c语言·数据结构·c++·数组·
QUST-Learn3D5 小时前
C++单头文件实现windows进程间通信(基于命名管道)
c++·windows·单片机
磨十三12 小时前
C++ 标准库排序算法 std::sort 使用详解
开发语言·c++·排序算法
湫兮之风14 小时前
C++: Lambda表达式详解(从入门到深入)
开发语言·c++
奔跑吧邓邓子14 小时前
【C++实战(54)】C++11新特性实战:解锁原子操作与异步编程的奥秘
c++·实战·c++11新特性·原子操作·异步编程