前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >一个合格C++程序员,应该善用智能指针!

一个合格C++程序员,应该善用智能指针!

作者头像
Linux兵工厂
发布2024-04-01 18:08:02
1050
发布2024-04-01 18:08:02
举报
文章被收录于专栏:Linux兵工厂Linux兵工厂

START

Hi,大家好!今天我们来聊一聊C++中的智能指针。

在谈到学习C++时,好多人都说它特别难,说它复杂。很可能有一部分原因就是C++的内存管理,在程序运行过程中很容易就会出现内存泄漏。然而从C++11引入的智能指针这一问题得到解决。

C++11引入了三种智能指针:

  • std::shared_ptr
  • std::weak_ptr
  • std::unique_ptr

unsetunset1、std::shared_ptrunsetunset

std::shared_ptr 是用于管理动态分配的资源,实现自动内存管理。它是一个引用计数型智能指针,允许多个 shared_ptr 对象共享同一块动态分配的内存,并在不再需要时自动释放。

以下是 std::shared_ptr 的一些重要特点和用法:

  1. 引用计数: std::shared_ptr 使用引用计数来跟踪共享的资源的使用情况。每个 std::shared_ptr 对象都包含一个计数器,记录有多少个 std::shared_ptr 对象共享同一块内存。
  2. 安全性: std::shared_ptr 通过引用计数机制来确保在所有持有该资源的 std::shared_ptr 对象被销毁后,资源会被释放。这避免了内存泄漏和空悬指针等问题。
  3. 拷贝和赋值: std::shared_ptr 支持拷贝和赋值操作,当进行拷贝时,计数器会增加;当进行销毁或重新赋值时,计数器会减少。当计数器减少到 0 时,资源会被释放。
  4. 动态分配的资源: std::shared_ptr 通常用于管理动态分配的资源,如内存、文件句柄等。它不仅可以管理指针指向的内存,还可以管理自定义的资源,如自定义的释放器等。
  5. 线程安全性: std::shared_ptr 在多线程环境下是线程安全的,可以被多个线程同时访问和操作,不需要额外的同步机制。

使用 std::shared_ptr 可以有效地管理动态分配的资源,避免内存泄漏和空悬指针等问题,并且可以方便地进行资源的共享和传递。然而,要注意避免循环引用的问题,这可能导致资源无法释放。通常可以使用 std::weak_ptr 来解决循环引用的问题。

下面来看一个使用 std::shared_ptr 的简单示例:

代码语言:javascript
复制
#include <iostream>
#include <memory>

class MyClass {
public:
    MyClass() {
        std::cout << "Constructor" << std::endl;
    }

    ~MyClass() {
        std::cout << "Destructor" << std::endl;
    }

    void doSomething() {
        std::cout << "Doing something..." << std::endl;
    }
};

int main() {
    // 创建一个动态分配的 MyClass 对象,并用 shared_ptr 管理
    std::shared_ptr<MyClass> ptr1(new MyClass);

    // 创建另一个 shared_ptr,与 ptr1 共享同一块内存
    std::shared_ptr<MyClass> ptr2 = ptr1;

    // 使用箭头运算符访问对象成员函数
    ptr1->doSomething();

    // 当 ptr1 和 ptr2 被销毁时,资源会被自动释放
    return 0;
}

在这个示例中,我们首先创建了一个动态分配的 MyClass 对象,并用 std::shared_ptr 管理它。然后,我们创建了另一个 std::shared_ptr,与第一个 std::shared_ptr 共享同一块内存。这意味着两个 std::shared_ptr 对象共享同一个计数器和同一块内存。最后,我们通过箭头运算符访问了 MyClass 对象的成员函数,并且在程序结束时,由于 ptr1ptr2 被销毁,MyClass 对象的资源会被自动释放。

这个示例展示了 std::shared_ptr 的基本用法,包括对象的创建、拷贝、访问成员函数以及自动资源管理。

unsetunset2、std::weak_ptrunsetunset

std::weak_ptr 是用于解决 std::shared_ptr 的循环引用问题。std::weak_ptr 允许共享资源但不拥有它,它是 std::shared_ptr 的弱引用。

std::shared_ptr 不同,std::weak_ptr 并不增加引用计数,因此它不会影响资源的生命周期。当最后一个 std::shared_ptr 指向的资源被释放后,所有相关联的 std::weak_ptr 对象都会自动失效,指向空指针。

以下是 std::weak_ptr 的一些重要特点和用法:

  1. 弱引用: std::weak_ptr 是一个弱引用,它不增加资源的引用计数,因此不会影响资源的生命周期。
  2. 共享资源: std::weak_ptr 允许与 std::shared_ptr 共享同一块内存,但不拥有它。它通常用于解决循环引用的问题,防止资源无法释放。
  3. 检查是否有效: 可以使用 std::weak_ptrexpired() 方法来检查与之关联的资源是否有效。如果资源已经释放,则 expired() 返回 true,否则返回 false
  4. 获取强引用: 可以使用 std::weak_ptrlock() 方法来获取与之关联的资源的强引用(即 std::shared_ptr 对象)。如果资源仍然有效,则 lock() 返回一个有效的 std::shared_ptr;如果资源已经释放,则返回一个空的 std::shared_ptr

以下是一个示例,展示了 std::weak_ptr 的基本用法:

代码语言:javascript
复制
#include <iostream>
#include <memory>

class MyClass {
public:
    MyClass() {
        std::cout << "Constructor" << std::endl;
    }

    ~MyClass() {
        std::cout << "Destructor" << std::endl;
    }
};

int main() {
    std::shared_ptr<MyClass> sharedPtr = std::make_shared<MyClass>();
    std::weak_ptr<MyClass> weakPtr = sharedPtr;

    // 检查 weakPtr 是否有效
    if (!weakPtr.expired()) {
        // 获取强引用
        std::shared_ptr<MyClass> strongPtr = weakPtr.lock();
        if (strongPtr) {
            // 访问资源
            std::cout << "Resource is valid." << std::endl;
        }
    }

    // sharedPtr 被销毁,资源释放
    sharedPtr.reset();

    // 再次检查 weakPtr 是否有效
    if (weakPtr.expired()) {
        std::cout << "Resource is expired." << std::endl;
    }

    return 0;
}

在这个示例中,我们首先创建了一个 std::shared_ptr 来管理动态分配的资源,然后创建了一个 std::weak_ptr 对象与之共享同一块内存。我们使用 expired() 方法检查了 std::weak_ptr 是否有效,并使用 lock() 方法获取了与之关联的资源的强引用。最后,我们释放了 sharedPtr,并再次检查了 std::weak_ptr 是否有效。

通过使用 std::weak_ptr,我们可以解决 std::shared_ptr 的循环引用问题,确保资源能够正确释放,避免内存泄漏。

unsetunset3、std::unique_ptrunsetunset

std::unique_ptr 是用于管理动态分配的资源。与 std::shared_ptr 不同,std::unique_ptr 是独占所有权的智能指针,即一个 std::unique_ptr 对象独占一个动态分配的资源,并负责在其生命周期结束时释放该资源。

以下是 std::unique_ptr 的一些重要特点和用法:

  1. 独占所有权: std::unique_ptr 是独占所有权的智能指针,一次只能有一个 std::unique_ptr 对象拥有一个动态分配的资源。
  2. 资源所有权转移: std::unique_ptr 支持资源所有权的转移。可以通过 std::move 函数将一个 std::unique_ptr 对象的所有权转移到另一个对象。
  3. 禁止拷贝和赋值: std::unique_ptr 对象禁止拷贝和赋值操作。这意味着不能对 std::unique_ptr 对象进行拷贝构造或赋值操作,从而确保资源的独占性。
  4. 动态分配的资源: std::unique_ptr 通常用于管理动态分配的资源,如内存、文件句柄等。它不仅可以管理指针指向的内存,还可以管理自定义的资源,如自定义的释放器等。
  5. 移动语义: std::unique_ptr 支持移动语义,可以高效地将资源转移给其他 std::unique_ptr 对象。这意味着在传递 std::unique_ptr 参数时,不会发生资源的拷贝,而是发生所有权的转移。

以下是一个示例,展示了 std::unique_ptr 的基本用法:

代码语言:javascript
复制
#include <iostream>
#include <memory>

class MyClass {
public:
    MyClass() {
        std::cout << "Constructor" << std::endl;
    }

    ~MyClass() {
        std::cout << "Destructor" << std::endl;
    }

    void doSomething() {
        std::cout << "Doing something..." << std::endl;
    }
};

int main() {
    // 创建一个动态分配的 MyClass 对象,并用 unique_ptr 管理
    std::unique_ptr<MyClass> ptr(new MyClass);

    // 调用对象的成员函数
    ptr->doSomething();

    // 当 ptr 被销毁时,资源会被自动释放
    return 0;
}

在这个示例中,我们首先创建了一个动态分配的 MyClass 对象,并用 std::unique_ptr 管理它。然后,我们通过箭头运算符调用了 MyClass 对象的成员函数,并且在程序结束时,由于 ptr 被销毁,MyClass 对象的资源会被自动释放。

通过使用 std::unique_ptr,我们可以方便地管理动态分配的资源,并避免内存泄漏和空悬指针等问题。

本文参与 腾讯云自媒体分享计划,分享自微信公众号。
原始发表:2024-03-23,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 Linux兵工厂 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体分享计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • unsetunset1、std::shared_ptrunsetunset
  • unsetunset2、std::weak_ptrunsetunset
  • unsetunset3、std::unique_ptrunsetunset
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档