首页 > 编程语言 >C++从小白到大牛:C++智能指针的使用、原理和分类

C++从小白到大牛:C++智能指针的使用、原理和分类

时间:2024-09-16 17:57:14浏览次数:13  
标签:std 白到 大牛 C++ 内存 shared unique ptr 指针

C++从小白到大牛:C++智能指针的使用、原理和分类

引言

在C++编程中,指针是一个强大但危险的工具。它们允许直接操作内存,但也可能导致内存泄漏、悬空指针等问题。为了解决这些问题,C++引入了智能指针(Smart Pointers)的概念。智能指针是一种封装了原始指针的对象,它们自动管理内存的生命周期,从而减少了手动管理内存的负担和风险。本文将详细介绍C++智能指针的使用、原理和分类,帮助你从小白成长为大牛。

智能指针的基本概念

什么是智能指针?

智能指针是一种对象,它封装了原始指针,并提供了自动内存管理功能。智能指针在对象超出作用域时会自动释放所管理的内存,从而避免了内存泄漏和悬空指针的问题。

智能指针的优势

  1. 自动内存管理:智能指针会在对象超出作用域时自动释放内存,减少了手动管理内存的负担。
  2. 避免内存泄漏:由于智能指针会自动释放内存,因此可以有效避免内存泄漏问题。
  3. 避免悬空指针:智能指针在释放内存后会自动将指针置为nullptr,避免了悬空指针的问题。
  4. 异常安全:智能指针可以在异常发生时自动释放内存,确保程序的异常安全性。

C++智能指针的分类

C++标准库提供了三种主要的智能指针:std::unique_ptrstd::shared_ptrstd::weak_ptr。每种智能指针都有其特定的用途和行为。

1. std::unique_ptr

基本概念

std::unique_ptr是一种独占所有权的智能指针。它确保只有一个std::unique_ptr实例可以拥有某个对象,当std::unique_ptr超出作用域时,它所管理的对象会被自动销毁。

使用示例
#include <iostream>
#include <memory>

class MyClass {
public:
    MyClass() { std::cout << "MyClass constructed" << std::endl; }
    ~MyClass() { std::cout << "MyClass destroyed" << std::endl; }
};

int main() {
    {
        std::unique_ptr<MyClass> ptr(new MyClass());
        // ptr owns the MyClass object
    } // ptr goes out of scope, MyClass object is destroyed

    return 0;
}
原理

std::unique_ptr通过RAII(Resource Acquisition Is Initialization)机制实现自动内存管理。当std::unique_ptr超出作用域时,它的析构函数会自动调用,从而释放所管理的对象。

注意事项
  • std::unique_ptr不能被复制,只能被移动。这意味着你不能将一个std::unique_ptr赋值给另一个std::unique_ptr,但可以通过std::move转移所有权。
  • std::unique_ptr适用于管理那些不需要共享所有权的对象。

2. std::shared_ptr

基本概念

std::shared_ptr是一种共享所有权的智能指针。多个std::shared_ptr实例可以共享同一个对象的所有权,当最后一个std::shared_ptr超出作用域时,对象才会被销毁。

使用示例
#include <iostream>
#include <memory>

class MyClass {
public:
    MyClass() { std::cout << "MyClass constructed" << std::endl; }
    ~MyClass() { std::cout << "MyClass destroyed" << std::endl; }
};

int main() {
    {
        std::shared_ptr<MyClass> ptr1 = std::make_shared<MyClass>();
        {
            std::shared_ptr<MyClass> ptr2 = ptr1;
            // Both ptr1 and ptr2 share ownership of the MyClass object
        } // ptr2 goes out of scope, but the MyClass object is not destroyed
    } // ptr1 goes out of scope, MyClass object is destroyed

    return 0;
}
原理

std::shared_ptr通过引用计数(Reference Counting)机制实现共享所有权。每个std::shared_ptr实例都会维护一个引用计数,当引用计数为零时,对象才会被销毁。

注意事项
  • std::shared_ptr可以被复制,多个std::shared_ptr实例可以共享同一个对象的所有权。
  • std::shared_ptr适用于管理那些需要共享所有权的对象。
  • 引用计数机制可能会导致循环引用问题,可以使用std::weak_ptr来解决。

3. std::weak_ptr

基本概念

std::weak_ptr是一种弱引用的智能指针。它不拥有对象的所有权,只是观察对象的存在。std::weak_ptr通常与std::shared_ptr一起使用,以避免循环引用问题。

使用示例
#include <iostream>
#include <memory>

class MyClass {
public:
    MyClass() { std::cout << "MyClass constructed" << std::endl; }
    ~MyClass() { std::cout << "MyClass destroyed" << std::endl; }
};

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

    {
        if (auto lockedPtr = weakPtr.lock()) {
            std::cout << "MyClass object still exists" << std::endl;
        } else {
            std::cout << "MyClass object has been destroyed" << std::endl;
        }
    } // lockedPtr goes out of scope, but the MyClass object is not destroyed

    sharedPtr.reset(); // MyClass object is destroyed

    if (auto lockedPtr = weakPtr.lock()) {
        std::cout << "MyClass object still exists" << std::endl;
    } else {
        std::cout << "MyClass object has been destroyed" << std::endl;
    }

    return 0;
}
原理

std::weak_ptr通过观察std::shared_ptr的引用计数来判断对象是否存在。它不会增加引用计数,因此不会影响对象的生命周期。

注意事项
  • std::weak_ptr不能直接访问对象,需要通过lock()方法获取一个std::shared_ptr实例。
  • std::weak_ptr适用于解决循环引用问题,避免内存泄漏。

智能指针的最佳实践

1. 优先使用std::make_uniquestd::make_shared

std::make_uniquestd::make_shared是创建std::unique_ptrstd::shared_ptr的首选方法。它们不仅更简洁,而且可以避免潜在的内存泄漏问题。

// 使用std::make_unique
std::unique_ptr<MyClass> ptr = std::make_unique<MyClass>();

// 使用std::make_shared
std::shared_ptr<MyClass> ptr = std::make_shared<MyClass>();

2. 避免 * 指针

尽量避免使用 * 指针(Raw Pointers),尤其是在管理动态分配的内存时。智能指针可以提供更安全和方便的内存管理。

3. 注意循环引用

在使用std::shared_ptr时,注意避免循环引用问题。如果两个对象互相持有对方的std::shared_ptr,可能会导致内存泄漏。可以使用std::weak_ptr来解决这个问题。

4. 使用std::weak_ptr解决循环引用

当两个对象互相持有对方的std::shared_ptr时,可以使用std::weak_ptr来打破循环引用。

class B;

class A {
public:
    std::shared_ptr<B> bPtr;
    ~A() { std::cout << "A destroyed" << std::endl; }
};

class B {
public:
    std::weak_ptr<A> aPtr;
    ~B() { std::cout << "B destroyed" << std::endl; }
};

int main() {
    std::shared_ptr<A> a = std::make_shared<A>();
    std::shared_ptr<B> b = std::make_shared<B>();

    a->bPtr = b;
    b->aPtr = a;

    return 0;
}

总结

智能指针是C++中管理动态内存的重要工具,它们提供了自动内存管理功能,避免了手动管理内存的复杂性和风险。通过合理使用std::unique_ptrstd::shared_ptrstd::weak_ptr,你可以编写更安全、更高效的C++代码。希望本文能帮助你从小白成长为大牛,掌握C++智能指针的使用、原理和分类。

标签:std,白到,大牛,C++,内存,shared,unique,ptr,指针
From: https://blog.51cto.com/u_17019724/12031756

相关文章

  • 揭开数据驱动的秘密:C++与数据中台的完美结合
    ......
  • 南沙C++信奥老师解一本通题 1371:看病
    ​ 【题目描述】有个朋友在医院工作,想请BSNY帮忙做个登记系统。具体是这样的,最近来医院看病的人越来越多了,因此很多人要排队,只有当空闲时放一批病人看病。但医院的排队不同其他排队,因为多数情况下,需要病情严重的人优先看病,所以希望BSNY设计系统时,以病情的严重情况作为优先级,判......
  • C++/Qt版餐厅点餐系统模块作品——演示(有需要的找我)
    点餐管理系统客户端APP代码展示顾客端(客户端)Widget.hChiddrom.hStrucural.hMain.cppWidget.cppChiddrom.cpp二、效果展示顾客端(客户端)厨房端(客户端)3、收银员(客户端)......
  • C++递归——蛇形方阵
    #题目描述#———————————————————思考线—————————————————————#思路分析#其实这道题并没有想象中那么麻烦,我们只需要关注第一轮也就是最外圈是如何赋值的即可。重难点:1.每一个断点到底是应该哪一个循环赋值?2.每一次换行第一个......
  • C++速通LeetCode简单第17题-爬楼梯
    思路要点:将问题转化为求斐波那契数列的第n项,然后迭代。思路分析:最后一次爬的阶数不是1就是2,假设爬n阶的方法数是f(n),假设最后一次爬1阶,那么爬前面的n-1阶的方法数是f(n-1);假设最后一次爬2阶,那么爬前面n-1阶的方法数是f(n-2)。所以可以得到:f(n)=f(n-1)+f(n-2),也就是斐波......
  • 【C++】模板进阶:深入解析模板特化
    C++语法相关知识点可以通过点击以下链接进行学习一起加油!命名空间缺省参数与函数重载C++相关特性类和对象-上篇类和对象-中篇类和对象-下篇日期类C/C++内存管理模板初阶String使用String模拟实现Vector使用及其模拟实现List使用及其模拟实现容器适配器Stack与Queue本章将......
  • 【C++】queue和priority_queue
    个人主页~queue和priority_queue一、queue的介绍和使用1、queue的介绍2、queue的使用3、queue的模拟实现二、priority_queue的介绍和使用1、priority_queue的介绍2、priority_queue的使用3、priority_queue的模拟实现三、仿函数1、仿函数的特征2、仿函数的使用ex、......
  • 用chatgpt让自己练习C++匿名函数
    再也不用去搜罗题目了,chatgpt1分钟搞定!如何使用ChatGPT帮助自己学习1、写prompt帮我写一个prompt,让LLM出一个c++专项训练的题目和题解,比如用户想学习C++的匿名函数,LLM就生成一个小型题目,题解全部是用C++匿名函数实现的。chatgpt:当然可以!以下是一个为训练C++匿名函......
  • C++ 成员函数指针简单测试
    classDog{public:voidUpdate_Func(shorti);short(Dog::*pfunc)(short);std::function<short(short)>ffunc;public:shortgoodMorning(shortid);shortgoodAfternoon(shortid);};voidDog::Update_Func(shorti){switch(i)......
  • 【GESP】C++一级练习BCQM3005,基本输出语句printf
    一道基础练习题,练习基本输出语句printf。BCQM3005题目要求描述输出表达式1234∗5678的结果。输入无输出1234∗5678=7006652输入样例无输出样例1234*5678=7006652全文详见个人独立博客:https://www.coderli.com/gesp-1-bcqm3005/【GESP】C++一级练习B......