C++高级特性:共享指针shared_ptr和弱指针weak_ptr(三)

1、共享指针shared_ptr
  • shared_ptr共享指针又称为计数指针,与unique_ptr不同的是它可以共享数据
  • shared_ptr创建了一个计数器与来对象所致的内存相关联
  • Copy则计数器加1,销毁则计数器减1,Move移动计数器不加不减
  • 可以通过use_count来查看当前有多少个计数指针。
1.1、计数问题
void test_shared_ptr1()
{
    std::shared_ptr<int> p1 = std::make_shared<int>(123);
    std::shared_ptr<int> p2 = p1;
    std::cout << "p1->val = " << *p1 << ", p1.use_count = " << p1.use_count() << std::endl;
    std::cout << "p2->val = " << *p2 << ", p2.use_count = " << p2.use_count() << std::endl;


    *p2 = 456;
    std::cout << "*p2 = 456;" << std::endl;
    std::cout << "p1->val = " << *p1 << ", p1.use_count = " << p1.use_count() << std::endl;
    std::cout << "p2->val = " << *p2 << ", p2.use_count = " << p2.use_count() << std::endl;

    auto p3 = p2;
    std::cout << "auto p3 = p2;" << std::endl;
    std::cout << "p1->val = " << *p1 << ", p1.use_count = " << p1.use_count() << std::endl;
    std::cout << "p2->val = " << *p2 << ", p2.use_count = " << p2.use_count() << std::endl;
    std::cout << "p3->val = " << *p3 << ", p2.use_count = " << p3.use_count() << std::endl;

    auto p4= std::move(p3);                 //移交所有权,不会进行拷贝或赋值重载
    std::cout << "auto p4= std::move(p3);" << std::endl;
    std::cout << "p1->val = " << *p1 << ", p1.use_count = " << p1.use_count() << std::endl;
    std::cout << "p2->val = " << *p2 << ", p2.use_count = " << p2.use_count() << std::endl;
//    std::cout << "p3->val = " << *p3 << ", p2.use_count = " << p3.use_count() << std::endl;           // 报错
    std::cout << "p4->val = " << *p4 << ", p4.use_count = " << p4.use_count() << std::endl;
}

/*		输出
p1->val = 123, p1.use_count = 2
p2->val = 123, p2.use_count = 2
*p2 = 456;
p1->val = 456, p1.use_count = 2
p2->val = 456, p2.use_count = 2
auto p3 = p2;
p1->val = 456, p1.use_count = 3
p2->val = 456, p2.use_count = 3
p3->val = 456, p2.use_count = 3
auto p4= std::move(p3);
p1->val = 456, p1.use_count = 3
p2->val = 456, p2.use_count = 3
p4->val = 456, p4.use_count = 3
*/
1.2、内存共享与销毁问题
  • shared_ptr指针所有对象只会创建一次,并且只会初始化一次,多次计数时只是多个指针指向了这一块地址
  • 析构时也只会析构一次,将这一块内存地址进行释放
  • 而reset()方法和指针修改为nullptr都只会把指向修改,并不会释放空间
void test_shared_ptr2()
{
    std::shared_ptr<Cat> p1 = std::make_shared<Cat>("小猫");
    std::shared_ptr<Cat> p2 = p1;
    std::shared_ptr<Cat> p3 = p1;

    p1.reset();
    std::cout << "p1.use_count = " << p1.use_count() << std::endl;
    std::cout << "p2.use_count = " << p2.use_count() << std::endl;
    std::cout << "p3.use_count = " << p3.use_count() << std::endl;

    p2 = nullptr;
    std::cout << "p1.use_count = " << p1.use_count() << std::endl;
    std::cout << "p2.use_count = " << p2.use_count() << std::endl;
    std::cout << "p3.use_count = " << p3.use_count() << std::endl;
    std::cout << __func__ << std::endl;
}
/*		输出
Constructor of Cat: 小猫
p1.use_count = 0
p2.use_count = 2
p3.use_count = 2
p1.use_count = 0
p2.use_count = 0
p3.use_count = 1
test_shared_ptr2				// 打印输出的函数名字
Destructor of Cat: 小猫
*/
1.3、值传递
  • 既然shared_ptr可以进行拷贝和赋值重载,那么就可以当值进行传入了
void pass_value(std::shared_ptr<Cat> ptr)
{
    std::cout << "pass_value():: ptr.use_count = " << ptr.use_count() << std::endl;
    ptr->set_Name("Mimi");
}

void test_shared_ptr3()
{
    std::shared_ptr<Cat> ptr = std::make_shared<Cat>("小猫");
    std::cout << "test_shared_ptr3():: ptr.use_count = " << ptr.use_count() << std::endl;
    pass_value(ptr);
    ptr->catInfo();
    std::cout << "test_shared_ptr3():: ptr.use_count = " << ptr.use_count() << std::endl;

}
/*
Constructor of Cat: 小猫
test_shared_ptr3():: ptr.use_count = 1
pass_value():: ptr.use_count = 2
cat info name: Mimi
test_shared_ptr3():: ptr.use_count = 1
Destructor of Cat: Mimi
*/
1.3、引用传递
  • 传递引用意义不是很大,而且容易出问题,需要防止提前空间释放的问题
void pass_ref(std::shared_ptr<Cat>& ptr)
{
    std::cout << "pass_value():: ptr.use_count = " << ptr.use_count() << std::endl;
    ptr->set_Name("Mimi");
    ptr.reset();
}

void test_shared_ptr4()
{
    std::shared_ptr<Cat> ptr = std::make_shared<Cat>("小猫");
    std::cout << "test_shared_ptr4():: ptr.use_count = " << ptr.use_count() << std::endl;
    pass_ref(std::ref(ptr));
//    ptr->catInfo();                   //报错
    std::cout << "test_shared_ptr4():: ptr.use_count = " << ptr.use_count() << std::endl;

}
/*
Constructor of Cat: 小猫
test_shared_ptr4():: ptr.use_count = 1
pass_value():: ptr.use_count = 1
Destructor of Cat: Mimi
test_shared_ptr4():: ptr.use_count = 0
*/
2、unique_ptr转换为shared_ptr
  • unique_ptr可以转化为shared_ptr,shared_ptr无法转化为unique_ptr。

  • 一个函数返回一个unique_ptr的智能指针有一个很强的兼容性,可以用unique_ptr接受也可以用shared_ptr接受

  • 其内部在返回时会将当前智能指针进行移动构造,也就是std::move的完全移交

std::unique_ptr<Cat> get_unique_ptr()
{
    std::unique_ptr<Cat> ptr = std::make_unique<Cat>("老猫");
    std::cout << "cat address = " << ptr.get() << std::endl;
    ptr->set_Name("Mimi");
    return ptr;
}

void test_shared_ptr5()
{
    std::shared_ptr<Cat> ptr = get_unique_ptr();
    std::cout << "cat address = " << ptr.get() << std::endl;
}
/*
Constructor of Cat: 老猫
cat address = 0x55d5fe193e70
cat address = 0x55d5fe193e70
Destructor of Cat: Mimi
*/
3、weak_ptr
  • weak_ptr:弱指针,它并不拥有指针的所有权,因此不能调用->和解引用*
  • weak_ptr无法单独创建实例的指针,一般是伴随着shared_ptr存在
3.1、弱指针的计数
void weak_ptr1()
{
    std::shared_ptr<Cat> sp = std::make_shared<Cat>("小猫");
    std::weak_ptr<Cat> wp(sp);

    std::cout << "sp.use_count = " << sp.use_count() << std::endl;
    std::cout << "wp.use_count = " << wp.use_count() << std::endl;

    sp.reset();
    std::cout << "sp.use_count = " << sp.use_count() << std::endl;
    std::cout << "wp.use_count = " << wp.use_count() << std::endl;
}
/*      输出
Constructor of Cat: 小猫
sp.use_count = 1
wp.use_count = 1
Destructor of Cat: 小猫
sp.use_count = 0
wp.use_count = 0
*/
void weak_ptr2()
{
    std::shared_ptr<Cat> sp = std::make_shared<Cat>("小猫");
    std::weak_ptr<Cat> wp(sp);

    std::cout << "sp.use_count = " << sp.use_count() << std::endl;
    std::cout << "wp.use_count = " << wp.use_count() << std::endl;

    wp.reset();
    std::cout << "sp.use_count = " << sp.use_count() << std::endl;
    std::cout << "wp.use_count = " << wp.use_count() << std::endl;
}
/*      输出
Constructor of Cat: 小猫
sp.use_count = 1
wp.use_count = 1
sp.use_count = 1
wp.use_count = 0
Destructor of Cat: 小猫
*/
3.2、函数调用与升级
  • 由于weak_ptr并不拥有指针的所有权,因此无法调用指针指向对象的函数
void weak_ptr3()
{
    std::shared_ptr<Cat> sp = std::make_shared<Cat>("小猫");
    std::weak_ptr<Cat> wp(sp);

    sp->catInfo();
//    wp->catInfo();          // 报错   因为没有重载->符号
    std::shared_ptr<Cat> sp2 = wp.lock();           // 需要调用时要调用lock进行升级成shared_ptr类型
    sp2->catInfo();
    std::cout << "sp.use_count = " << sp.use_count() << std::endl;
    std::cout << "wp.use_count = " << wp.use_count() << std::endl;
    std::cout << "sp2.use_count = " << sp2.use_count() << std::endl;
}
/*
Constructor of Cat: 小猫
cat info name: 小猫
cat info name: 小猫
sp.use_count = 2
wp.use_count = 2
sp2.use_count = 2
Destructor of Cat: 小猫
*/
3.3、循环依赖问题
  • 循环依赖:面向对象程序中非常常见的一个问题,类A依赖类B,类B依赖类A或者多个类形成环形就叫循环依赖
  • 如果使用shared_ptr在销毁时就会遇到循环依赖问题,Java语言在构建对象时会有循环依赖的问题(Spring框架用三级缓存解决)
class Teacher;

class School{
public:
    std::string name;
    int age;
    std::shared_ptr<Teacher> headTeacher;
    virtual ~School(){
        std::cout << "School Destructed!" << std::endl;
    }
};
class Teacher{
public:
    std::string name;
    std::shared_ptr<School> school;
    virtual ~Teacher(){
        std::cout << "Teacher Destructed!" << std::endl;
    }
};

void weak_ptr4()
{
    std::shared_ptr<School> university = std::make_shared<School>();
    std::shared_ptr<Teacher> teacher = std::make_shared<Teacher>();
    university->headTeacher = teacher;
    teacher->school = university;
    std::cout << __func__ << std::endl;
}
// 只输出:weak_ptr4
  • 很明显这里会发现university和teacher相互依赖,导致shared_ptr的计数引用无法降为0形成了死锁。

  • 在释放对象时二者都不肯先一步析构,最后无法析构导致内存泄漏

  • 解决方案:只需要将其中一个的shared_ptr弱化成weak_ptr指针即可。

  • 1
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值