智能指针(二) shared_ptr 注意点
1 不存在 int * 到 shared_ptr 的隐式类型转换
void proc(shared_ptr<int> ptr)
{
cout << "ptr.use_count()=" << ptr.use_count() << endl;
cout << "调用成功" << endl;
return;
}
int *p = new int(100);
// todo 1 不存在 int * 到 shared_ptr<int> 的隐式类型转换
// proc(p);
2切勿传递临时引用作为函数参数
void proc(shared_ptr<int> ptr)
{
cout << "ptr.use_count()=" << ptr.use_count() << endl;
cout << "调用成功" << endl;
return;
}
int *p = new int(100);
proc(std::shared_ptr<int>(p)); // 参数是临时的 shared_ptr,用裸指针显示构造
// 传递到 proc 函数中的引用计数为 1 ,离开函数体后,引用计数为 0 , 临时构造的shared_ptr指针所指向的内存(也就是p 指向的内存) 被 回收
*p =25; //此时操作p将是违法的(已置空)
- 把一个普通裸指针绑定到一个shared_ptr 上之后, 内存管理的责任就交给这个shared_ptr了,这个时候就不应该用裸指针来访问shared_ptr所指向的内存了
修正的做法是
shared_ptr<int> pi(p);
proc(pi);
*pi = 45; // 操作安全
// 传递到 proc 函数中的引用计数为 2 ,离开函数体后,引用计数为 1 ,pi 指向的对象 不会被回收。
3严禁 用裸指针初始化多个 shared_ptr
- 多个shared_ptr 并不会增加引用计数,内存管理将不会贯通(初始化 shared_ptr,但二者并不指向同一个控制块,引用计数不会累计),
int *p = new int(100);
void func()
{
shared_ptr<int> p1(p);
cout << "p1.use_count()" << p1.use_count() << endl;
shared_ptr<int> p2(p);
cout << "p1.use_count()" << p1.use_count() << endl;
// 当 p1 p2 的生命结束时, 指向的对象将被 释放两次(因为p1和p2引用计数均为1,而且p2 是释放同一块已经释放内存的空间)
// 应用下面的方式代替
/*
shared_ptr<int> p1(new int); // 用匿名裸指针 初始化
shared_ptr<int> p2(p1); //用p1 初始化 其他 shared_ptr
*/
}
4谨慎 使用 get 返回的裸指针
- 注意 :get() 返回的裸指针不能delete,否则会异常
void func()
{
cout << "detail2::func()" << endl;
shared_ptr<int> pi(new int);
int *p = pi.get();
delete p;// error
}
- 不能将智能指针 绑定到get 返回的指针上
void func2()
{
// ?. 类似于把裸指针赋值给了两个shared_ptr
shared_ptr<int> pi(new int);
cout << "pi.use_count()" << pi.use_count() << endl;
int *p = pi.get();
shared_ptr<int> pi2(p);
cout << "pi.use_count()" << pi.use_count() << endl;
cout << "pi2.use_count()" << pi2.use_count() << endl;
}
5 不要返回类对象指针
- 使用类对象this初始化另一个对象, 引用计数将不会增加
class TC
{
public:
shared_ptr<TC> get_self_this()
{
return shared_ptr<TC>(this);
}
};
void func()
{
cout << "detail::func()" << endl;
shared_ptr<TC> p1(new TC);
cout << "p1.use_count()" << p1.use_count() << endl;
shared_ptr<TC> p2 = p1->get_self_this();
cout << "p1.use_count()" << p1.use_count() << endl;
}
- 使用 shared_from_this 代替
class TC2 : public enable_shared_from_this<TC2>
{
public:
shared_ptr<TC2> get_self_enable_shared()
{
// /这个就是enable_shared_from_this类中的方法,要通过此方法返回智能指针
return shared_from_this();
}
};
void func2()
{
cout << "detail2::func2()" << endl;
shared_ptr<TC2> p1(new TC2);
cout << "p1.use_count()" << p1.use_count() << endl;
shared_ptr<TC2> p2 = p1->get_self_enable_shared();
cout << "p1.use_count()" << p1.use_count() << endl;
}
6 循环引用问题
class CB;
class CA
{
public:
CA() { cout << "CA() called! " << endl; }
~CA() { cout << "~CA() called! " << endl; }
void set_ptr(shared_ptr<CB> &ptr) { m_ptr_b = ptr; }
void b_use_count() { cout << "b use count : " << m_ptr_b.use_count() << endl; }
void show() { cout << "this is class CA!" << endl; }
private:
shared_ptr<CB> m_ptr_b;
};
class CB
{
public:
CB() { cout << "CB() called! " << endl; }
~CB() { cout << "~CB() called! " << endl; }
void set_ptr(shared_ptr<CA> &ptr) { m_ptr_a = ptr; }
void a_use_count() { cout << "a use count : " << m_ptr_a.use_count() << endl; }
void show() { cout << "this is class CB!" << endl; }
private:
shared_ptr<CA> m_ptr_a;
};
void test_refer_to_each_other()
{
shared_ptr<CA> ptr_a(new CA());
shared_ptr<CB> ptr_b(new CB());
cout << "a use count : " << ptr_a.use_count() << endl;
cout << "b use count : " << ptr_b.use_count() << endl;
ptr_a->set_ptr(ptr_b);
ptr_b->set_ptr(ptr_a);
cout << "a use count : " << ptr_a.use_count() << endl;
cout << "b use count : " << ptr_b.use_count() << endl;
}
int main()
{
test_refer_to_each_other();
}
CA() called!
CB() called!
a use count : 1
b use count : 1
a use count : 2
b use count : 2
- 通过结果可以看到,最后CA和CB的对象并没有被析构,其中的引用效果如下图所示,起初定义完ptr_a和ptr_b时,只有 ptr_a 指向new_CA() 和ptr_b 指向 new CB()两条引用,
- 然后调用函数set_ptr后又增加了m_ptr_a 指向 new CA() 和 m_ptr_b指向 new CB 两条引用,当test_refer_to_each_other这个函数返回时,对象ptr_a和ptr_b被销毁,
- 也就是【ptr_a 指向new_CA() 和ptr_b 指向 new CB()两条引用 】会被断开,但是依然m_ptr_a 指向 new CA() 和 m_ptr_b指向 new CB 两条引用存在,
- 每一个的引用计数都不为0,结果就导致其指向的内部对象无法析构(不能调用析构函数),造成内存泄漏。
解决这种状况的办法就是将两个类中的一个成员变量改为weak_ptr
对象,因为weak_ptr
不会增加引用计数,使得引用形不成环,最后就可以正常的释放内部的对象,不会造成内存泄漏,比如将CB
中的成员变量改为weak_ptr
对象,代码如下:
class CB;
class CA
{
public:
CA() { cout << "CA() called! " << endl; }
~CA() { cout << "~CA() called! " << endl; }
void set_ptr(shared_ptr<CB> &ptr) { m_ptr_b = ptr; }
void b_use_count() { cout << "b use count : " << m_ptr_b.use_count() << endl; }
void show() { cout << "this is class CA!" << endl; }
private:
shared_ptr<CB> m_ptr_b;
};
/*
todo 解决这种状况的办法就是将两个类中的一个成员变量改为weak_ptr对象,因为weak_ptr不会增加引用计数,使得引用形不成环,
todo 最后就可以正常的释放内部的对象,不会造成内存泄漏,比如将CB中的成员变量改为weak_ptr对象,代码如下:
*/
class CB
{
public:
CB() { cout << "CB() called! " << endl; }
~CB() { cout << "~CB() called! " << endl; }
void set_ptr(shared_ptr<CA> &ptr) { m_ptr_a = ptr; }
void a_use_count() { cout << "a use count : " << m_ptr_a.use_count() << endl; }
void show() { cout << "this is class CB!" << endl; }
private:
weak_ptr<CA> m_ptr_a;
};
void test_refer_to_each_other()
{
shared_ptr<CA> ptr_a(new CA());
shared_ptr<CB> ptr_b(new CB());
cout << "a use count : " << ptr_a.use_count() << endl;
cout << "b use count : " << ptr_b.use_count() << endl;
ptr_a->set_ptr(ptr_b);
ptr_b->set_ptr(ptr_a);
cout << "a use count : " << ptr_a.use_count() << endl;
cout << "b use count : " << ptr_b.use_count() << endl;
}
测试结果如下:
CA() called!
CB() called!
a use count : 1
b use count : 1
a use count : 1
b use count : 2
~CA() called!
~CB() called!
- 通过这次结果可以看到,CA和CB的对象都被正常的析构了,*
- 但是不同的是m_ptr_a指向new CA()这条引用是通过weak_ptr建立的,并不会增加引用计数,也就是说CA的对象只有一个引用计数,*
- t而CB的对象只有2个引用计数,当test_refer_to_each_other这个函数返回时,对象ptr_a和ptr_b被销毁,
- 也就是 ptr_a 指向new_CA() 和ptr_b 指向 new CB()两条引用 会被断开,此时CA对象的引用计数会减为0,对象被销毁,*
- *其内部的m_ptr_b成员变量也会被析构,导致CB对象的引用计数会减为0,对象被销毁,进而解决了引用成环的问题。
7移动语义
- 使用 std::move 可以将智能指针拥有权 转交给另一个智能指针,而不引起引用计数的增加
void func()
{
cout << "detail6::func" << endl;
shared_ptr<int> si = make_shared<int>(100);
cout << "si.use_count()" << si.use_count() << endl;
auto si2 = si;
cout << "si.use_count()" << si.use_count() << endl;
cout << "si2.use_count()" << si2.use_count() << endl;
}
void func2()
{
cout << "detail6::func2" << endl;
shared_ptr<int> si = make_shared<int>(100);
cout << "si.use_count()" << si.use_count() << endl;
auto si2 = std::move(si);
cout << "si.use_count()" << si.use_count() << endl;
cout << "si2.use_count()" << si2.use_count() << endl;
}
<< “detail6::func2” << endl;
shared_ptr si = make_shared(100);
cout << “si.use_count()” << si.use_count() << endl;
auto si2 = std::move(si);
cout << "si.use_count()" << si.use_count() << endl;
cout << "si2.use_count()" << si2.use_count() << endl;
}