C++ | C++11中的智能指针(附模拟实现):现代的C++内存管理

目录

10--智能指针

1、内存管理的挑战和智能指针的必要性:

2、内存泄漏:

3、智能指针和原理:

4、C++标准库中的智能指针:

std::auto_ptr:C++98中的智能指针,存在问题,不推荐使用。

std::unique_ptr:C++11中引入,不允许拷贝,只能移动。

std::shared_ptr:通过引用计数管理资源,支持多个智能指针共享同一资源。

weak_ptr:解决循环引用问题

5、智能指针的高级特性:

6、C++11和Boost库中智能指针的关系:

7、题目


10--智能指针

在C++编程中,内存管理一直是一个复杂而关键的话题。传统的newdelete操作虽然直接,但很容易导致内存泄漏和其他资源管理问题。随着C++的发展,利用RAII的智能指针作为现代C++内存管理的解决方案应运而生。智能指针是C++内存管理的一大进步,它们提供了一种安全、高效的方式来自动管理动态分配的内存减少内存泄漏的风险,同时简化代码的复杂性。

1、内存管理的挑战和智能指针的必要性

在传统C++编程中,内存管理是一项至关重要的任务。传统的手动内存管理方式虽然灵活,但容易出错。例如,例如,当new操作抛出异常时,程序可能在运行中途终止,如果没有适当的异常处理和内存释放机制,就可能导致内存泄漏。为了解决这一问题,C++11标准引入了智能指针,它们提供了自动内存管理的机制,极大地简化了资源管理的复杂性。

2、内存泄漏

内存泄漏发生在程序未能释放不再使用的内存时。长期运行的程序如果出现内存泄漏,会逐渐变慢,甚至崩溃。智能指针通过自动管理内存,有效避免了内存泄漏的问题。发生内存泄露的场景主要集中在异常安全问题。

#include<iostream>
using namespace std;
double Division(double a, double b)
{
    if (b == 0)
        throw "number b == 0";
    return a / b;
}
int main()
{
    int* a = new int(10);
    int* b = new int(0);
    Division(*a, *b);

    // 异常抛出未处理,a、b将无法释放,导致内存泄露
    delete a;
    delete b;

    return 0;
}

避免内存泄漏的方法

  • 良好的设计规范和编码习惯(也不好弄)
  • 使用RAII(Resource Acquisition Is Initialization)技术和智能指针。

3、智能指针和原理
  • RAII(Resource Acquisition Is Initialization):资源在对象构造时获取,在析构时释放。

RAII是一种利用对象的生命周期来管理资源的技术。它的核心思想是在对象构造时获取资源,并在对象析构时释放资源。这样,资源的管理和对象的生命周期紧密相连,确保了资源的正确管理。

4、C++标准库中的智能指针
std::auto_ptr:C++98中的智能指针,存在问题,不推荐使用。

auto_ptr 是c++ 98定义的智能指针模板,其定义了管理指针的对象,可以将new 获得(直接或间接)的地址赋给这种对象。当对象过期时,其析构函数将使用delete 来释放内存

头文件:<menory>

  1. get(): 返回指向 auto_ptr 管理的对象的指针。这个函数不会转移所有权,只是返回指针。
  2. release(): 返回指向 auto_ptr 管理的对象的指针,并将 auto_ptr 置为空(不再管理对象)。这个函数会转移所有权,所以在调用后,你需要自己负责释放对象的内存。
  3. reset():用于将 auto_ptr 置为空,不再管理对象。它的作用类似于 release(),但是不返回指向对象的指针。相反,它只是将 auto_ptr 置为空,释放对对象的所有权。

Example:

#include<iostream>
#include<memory>
using namespace std;
int main()
{
    auto_ptr<int> autoPtr(new int);
    cout<<*autoPtr<<endl; // 325344784
    *autoPtr = -1;
    cout<<*autoPtr<<endl; // -1

    cout<<autoPtr.get()<<endl; // 0x25513641ab0
    int* p = autoPtr.release();
    cout<<*p<<endl; // -1
    autoPtr.reset(new int(100));
    cout<< *autoPtr<<endl; // 100

    return 0;
}

auto_ptr没有完全解决拷贝时绑定资源的问题(主要是资源的管理和释放),而是使用管理权转移的手段

!!! 自从 C++11 之后,auto_ptr 已被 unique_ptr 取代,C++11后不建议使用auto_ptr

std::unique_ptr:C++11中引入,不允许拷贝,只能移动。

unique_ptr直接禁止拷贝构造、赋值运算符重载等可能涉及资源转移的操作

注意:unique_ptr构造的时候不能使用等号!!!

No viable conversion from 'Date *' to 'unique_ptr<Date>'

#include<iostream>
using namespace std;
int main()
{
    //unique_ptr<Date> p = new Date; // error(unique_ptr不支持赋值)
    unique_ptr<Date> p(new Date); // 成功
    return 0;
}

源码实现

#pragma once
namespace my_stl
{
    template<class T>
    class unique_ptr
    {
    private:
        T* _ptr;
    public:
        unique_ptr() {}

        unique_ptr(T* ptr)
            :_ptr(ptr)
        {}

        ~unique_ptr()
        {
            if (_ptr) // 检查内部指针情况
            {
                delete _ptr;
                _ptr = nullptr;
            }
        }

        unique_ptr<T>(const unique_ptr<T>& p) = delete;
        unique_ptr<T>& operator=(const unique_ptr<T>& p) = delete;

        T& operator*()
        {
            return *_ptr;
        }

        T* operator->()
        {
            return _ptr;
        }
    };
}

std::shared_ptr:通过引用计数管理资源,支持多个智能指针共享同一资源。

源码实现:在unique_ptr的基础上添加拷贝构造和赋值运算符重载即可

[warring]此代码没有考虑线程安全!

#pragma once
namespace my_stl
{
    template<class T>
    class shared_ptr
    {
        private:
            int* _count;
            T* _ptr;
        public:
            shared_ptr()
                :_count(new int(0)),
                _ptr(nullptr)
            {}
            
            shared_ptr(T* ptr)
                :_count(new int(1)),
                _ptr(ptr)
            {}

            shared_ptr(const shared_ptr& sp)
                :_count(sp._count),
                _ptr(sp._ptr)
            {
                ++(*_count);
            }

            shared_ptr& operator=(const shared_ptr& sp)
            {
                if (this != &sp)
                {
                    this->~shared_ptr();
                    _ptr = sp._ptr;
                    _count = sp._count;
                    ++(*sp._count);
                }
                return *this;
            }

            ~shared_ptr()
            {
                if (--(*_count) == 0 && _ptr)
                {
                    delete _ptr;
                    delete _count;
                    _ptr = _count = nullptr;
                }
            }

            T& operator*()
            {
                return *_ptr;
            }

            T* operator->()
            {
                return _ptr;
            }
    };
}
weak_ptr:解决循环引用问题

weak_ptr是一种不控制对象生命周期的智能指针,它可以用来解决std::shared_ptr循环引用导致内存泄漏的问题。但由于实际项目中不经常使用,在此基础篇不过多介绍。

5、智能指针的高级特性
  • std::shared_ptr的线程安全问题:引用计数的修改是线程安全的,但资源本身不是。
  • 循环引用问题:使用weak_ptr解决shared_ptr的循环引用问题。
  • 删除器:shared_ptr可以与删除器一起使用,以管理非堆对象。

6、C++11和Boost库中智能指针的关系

C++的Boost库是一个广泛使用的C++库集合,它提供了对标准C++库的补充。Boost库由一群C++开发者创建,旨在为C++程序员提供高质量的、经过同行评审的库,这些库最终可能成为C++标准的一部分。(可以理解为boost是c++标准的beta测试版)

  • C++98引入了auto_ptr
  • Boost库提供了scoped_ptrshared_ptrweak_ptr
  • C++11标准从Boost库中借鉴并引入了unique_ptrshared_ptrweak_ptr

7、题目

(1)下面关于unique_ptr说法错误的是(B)

A.unique_ptr是C++11才正式提出的

B.unique_ptr可以管理一段连续空间

C.unique_ptr不能使用其拷贝构造函数

D.unique_ptr的对象之间不能相互赋值

解释:B错误,C++11中提供的智能指针都只能管理单个对象的资源,没有提供管理一段空间资源的智能指针.

(2)关于RAII下面说法错误的是(C)

A.RAII的实现方式就是在构造函数中将资源初始化,在析构函数中将资源清理掉

B.RAII方式管理资源,可以有效避免资源泄漏问题

C.所有智能指针都借助RAII的思想管理资源

D.RAII方式管理锁,有些场景下可以有效避免死锁问题

解释:C错误,weak_ptr不能单独管理资源,必须配合shared_ptr一块使用,解决shared_ptr中存在的循环引用问题

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值