C++应用系列:用智能指针shared_ptr中引用计数思想进行动态内存管理

本文详细解释了C++中深拷贝与浅拷贝的概念及其重要性,通过实例展示了如何手动实现拷贝构造函数和拷贝赋值运算符来管理动态内存,避免内存泄漏等问题。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

如果在类的定义中不自己定义默认拷贝赋值构造函数与析构函数,编译器会自动创建合成函数,在初始化时使用类内初始值或默认值初始化成员。
编译器自动生成提供的默认拷贝构造函数是浅拷贝,所谓浅拷贝,则是简单将成员进行赋值操作,如果成员中存在指针或引用时,执行浅拷贝则只是将指针或引用赋值,而具体指向的是同一内存空间,在修改值时会出现意想不到的问题,而且,最重要是在析构时同一内存单元重复释放两次。

当我们在堆中存储动态分配的对象,即使用动态内存时,通常容易出现3种错误:
1.忘记delete之前new的内存,即内存泄漏。
2.使用空悬指针访问释放掉的内存。
3.重复释放同一内存单元。

智能指针shared_ptr中有一个名为引用计数的计数器,记录指向某同块内存的shared_ptr,当计数器为0时自动调用析构函数释放内存,这样能够有效避免上述问题,具体用法网上书上较多资料,此处不赘述。

当类中存在动态内存时,可以自己定义类的拷贝构造函数以及拷贝赋值运算符,进行深拷贝。使每个对象管理独立的内存,并在析构中释放该内存。
C++基础系列:深拷贝浅拷贝函数与拷贝赋值操作符

//class Rect
//带有int *scale成员
    Rect(T a, T b) :m_lenth(a), m_width(b)
    {
        scale = new int;
    }
    Rect(const Rect<T> &rect)
    {
        cout << "重载拷贝" << endl;
        scale = new int;
        *scale = rect.getScale();
        m_lenth = rect.getLength();
        m_width = rect.getWidth();
    }
    ~Rect()
    {
        delete scale;
    }
    Rect<T>& operator=(const Rect<T> &rect)
    {
        cout << "重载赋值" << endl;
        scale = new int;
        *scale = rect.getScale();
        m_lenth = rect.getLength();
        m_width = rect.getWidth();
        return *this;
    }

也可以利用引用计数的思路进行动态内存的管理,多个指针指向相同的内存块,但是只有在最后一个指针销毁时,才释放该内存。

//定义一个矩形类模板
//模板中含有计算矩形面积和周长的成员函数
//数据成员为矩形的长和宽。

#include <iostream>

using namespace std;

template <typename T>
class Rect
{
public:
    Rect(T a,T b):scale(new int(0)),    //每个对象直接构造时new内存
                  ref_cnt(new size_t(1))
    {
        cout << "构造" << endl;
    }
    Rect(const Rect<T> &rect) :scale(rect.scale),   //拷贝与赋值时不开辟新内存,指向同一块内存
                               ref_cnt(rect.ref_cnt)
    {
        cout << "重载拷贝" << endl;
        ++*ref_cnt;
    }
    ~Rect()
    {
        if(--*ref_cnt == 0)     //每个对象析构后引用数-1,为0则释放内存
        {
            delete ref_cnt;
            delete scale;
        }
    }
    Rect<T>& operator=(const Rect<T> &rect)
    {
        cout << "重载赋值" << endl;
        ++*ref_cnt;             //等号右侧赋值后引用数+1
        if (--*ref_cnt == 0)    //等号左侧赋值后引用数-1
        {
            delete ref_cnt;
            delete scale;
        }
        ref_cnt = rect.ref_cnt;
        scale = rect.scale;
        return *this;
    }
    void setScale(int _scale)
    {
        *scale = _scale;
    }
private:
    int *scale;
    size_t *ref_cnt;
};

template <typename T>
Rect<T> doNothing(const Rect<T> &rect)
{
    return rect;
}

int main(void)
{
    Rect<int> rect(0,0);//构造
    rect.setScale(1);

    Rect<int> rect2(0,0);//构造
    rect2 = rect;           //赋值

    Rect<int> rect3 = rect; //调用拷贝构造函数

    rect.setScale(2);

    Rect<int> rect4 = doNothing(rect);//拷贝
    Rect<int> rect5(0, 0);//先构造,调用带参数构造函数
    rect5 = doNothing(rect);//调用拷贝构造函数,再调用拷贝赋值


    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值