【C++11智能指针】shared_ptr的使用陷阱、尺寸

1.不要混合使用普通指针和智能指针

接受指针参数的智能指针构造函数是 explicit 的,因此,我们不能将一个内置指针隐式转换为一个智能指针,必须使用直接初始化形式来初始化一个智能指针。

#include <iostream>
#include <memory>
using namespace std;

void fun(shared_ptr<int> sp)
{
    return;
}

int main()
{
    int* p = new int(100); // 裸指针

    fun(p); // 语法错误

    return 0;
}

避免使用匿名的临时 shared_ptr 对象。

#include <iostream>
#include <memory>
using namespace std;

void fun(shared_ptr<int> sp)
{
    return;
}

int main()
{
    int* p = new int(100); // 裸指针

    fun(shared_ptr<int>(p)); // 实参是用裸指针显式构造的临时的shared_ptr

    // 将实参传递给形参sp之后,强引用计数从0变1,但是离开fun()函数之后,强引用计数从1变0,此时再*p就会存在不可预料的问题,因为它早就被释放了
    *p = 45;

    return 0;
}

把一个裸指针绑定到一个 shared_ptr 上之后,内存管理的责任就交给这个 shared_ptr 了,此时就不应该再用裸指针来访问这块内存了。

#include <iostream>
#include <memory>
using namespace std;

void fun(shared_ptr<int> sp)
{
    return;
}

int main()
{
    int* p = new int(100); // 裸指针

    shared_ptr<int> sp(p); // 强引用计数从0变1

    fun(sp); // 进入fun()函数之后,强引用计数从1变2,进入fun()函数之后,强引用计数从2变1

    *sp = 100; // 此时赋值没有问题

    return 0;
}

不要用裸指针初始化多个 shared_ptr。

#include <iostream>
#include <memory>
using namespace std;

int main()
{
    int* p = new int(100); // 裸指针

    shared_ptr<int> sp1(p); // sp1的强引用计数从0变1

    shared_ptr<int> sp2(p); // sp2的强引用计数从0变1

    // sp1和sp2指向同一块内存,但sp1和sp2无关联,会导致sp1和sp2所指向的内存被释放两次

    return 0;
}
#include <iostream>
#include <memory>
using namespace std;

int main()
{
    shared_ptr<int> sp1(new int(100)); // 强引用计数从0变1

    shared_ptr<int> sp2(sp1); // 强引用计数从1变2

    // sp1和sp2指向同一块内存,两者是互通的

    return 0;
}

2.不要使用get()初始化另一个智能指针或为智能指针赋值

#include <iostream>
#include <memory>
using namespace std;

int main()
{
    shared_ptr<int> sp1(new int(110)); // sp1的引用计数从0变1

    int* p = sp1.get();

    {
        shared_ptr<int> sp2(p); // sp2的引用计数从0变1,此时sp1和sp2的引用计数都为1且指向相同的内存
    } // 当程序块结束时,sp2指向的内存被释放

    *sp1 = 120; // 该内存已经被释放,这样赋值会导致不可预料的后果

    return 0;
}

3.不要把类对象指针this作为shared_ptr返回

#include <iostream>
#include <memory>
using namespace std;

class CT
{
public:
    shared_ptr<CT> getSelf()
    {
        return shared_ptr<CT>(this);
    }
};

int main()
{
    shared_ptr<CT> sp1(new CT); // sp1的强引用计数从0变1

    shared_ptr<CT> sp2 = sp1->getSelf(); // sp2的强引用计数从0变1
    
    // sp1和sp2指向相同的内存,但sp1和sp2无关联,会导致sp1和sp2所指向的内存被释放两次

    return 0;
}

解决方法:使用 C++ 标准库里面的类模板 enable_shared_from_this

#include <iostream>
#include <memory>
using namespace std;

class CT : public enable_shared_from_this<CT>
{
public:
    shared_ptr<CT> getSelf()
    {
        return shared_from_this(); // shared_from_this()方法实际上是调用了内部的weak_ptr的lock()方法
    }
};

int main()
{
    shared_ptr<CT> sp1(new CT); // 强引用计数从0变1

    shared_ptr<CT> sp2 = sp1->getSelf(); // 强引用计数从1变2

    // sp1和sp2指向相同的内存,两者是互通的

    return 0;
}

4.循环引用导致内存泄漏

#include <iostream>
#include <memory>
using namespace std;

class Person; // 声明一下Person类

class Car
{
public:
	shared_ptr<Person> m_person;
	Car() { cout << "Car()" << endl; }
	~Car() { cout << "~Car()" << endl; }
};

class Person
{
public:
	shared_ptr<Car> m_car;
	Person() { cout << "Person()" << endl; }
	~Person() { cout << "~Person()" << endl; }
};

int main()
{
	shared_ptr<Person> person(new Person()); // Person对象的强引用计数从0变1
	shared_ptr<Car> car(new Car()); // Car对象的强引用计数从0变1

	car->m_person = person; // Person对象的强引用计数从1变2
	person->m_car = car; // Car对象的强引用计数从1变2

	return 0;
}

循环引用导致内存泄漏:

在这里插入图片描述

引用关系如下:

在这里插入图片描述

5.weak_ptr解决循环引用

#include <iostream>
#include <memory>
using namespace std;

class Person; // 声明一下Person类

class Car
{
public:
	weak_ptr<Person> m_person;
	Car() { cout << "Car()" << endl; }
	~Car() { cout << "~Car()" << endl; }
};

class Person
{
public:
	shared_ptr<Car> m_car;
	Person() { cout << "Person()" << endl; }
	~Person() { cout << "~Person()" << endl; }
};

int main()
{
	shared_ptr<Person> person(new Person()); // Person对象的强引用计数从0变1
	shared_ptr<Car> car(new Car()); // Car对象的强引用计数从0变1

	car->m_person = person; // Person对象的强引用计数仍为1,弱引用计数从0变1
	person->m_car = car; // Car对象的强引用计数从1变2

	return 0;
}

输出结果如下:

在这里插入图片描述

引用关系如下:

在这里插入图片描述

6.shared_ptr的尺寸

shared_ptr 和 weak_ptr 的尺寸都是裸指针的两倍。

在这里插入图片描述

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值