您的位置:首页 > 编程语言 > C语言/C++

C++11新特性之智能指针

2018-03-18 22:25 471 查看
本文章参考https://www.cnblogs.com/feng-sc/p/5710724.html#title33。因为其对智能指针介绍的比较详细,在自己的工作中,智能指针用的少,都是new delete用的多,实际开发中,不少情况下会出现内存泄漏的问题,都是因为只有new 没有delete导致的。智能指针为我们解决了资源管理的问题,让我们不再担心资源泄漏的问题。但是智能指针难道就不会存在资源泄漏的问题吗 ? 答案是NO,在用shared_ptr的时候,如果出现相互引用的情况下,shared_ptr就不能释放资源。

智能指针只是用对象去管理一个资源指针,同时用一个计数器计算当前指针引用对象的个数,当管理指针的对象增加或减少时,计数器也相应加1或减1,当最后一个指针管理对象销毁时,计数器为1,此时在销毁指针管理对象的同时,也把指针管理对象所管理的指针进行delete操作。

下面是shared_ptr的程序demo。

#include <memory>

class Person
{
public:
Person()
{
cout << "Person default constructor" << endl;
}
~Person()
{
std::cout << "~Person destructor" << endl;
}
};

int main(int argc, char** argv)
{
shared_ptr<Person> p1 = make_shared<Person>();
cout << "p 1 ref count:" << p1.use_count() << endl;
{
std::shared_ptr<Person> p2 = p1;
std::cout << "p 2 ref count:" << p1.use_count() << std::endl;
}
std::cout << "p 3 ref count:" << p1.use_count() << std::endl;

return 0;
}


运行结果:



从上面的代码中,可知:

1、std::make_shared封装了new方法,boost::make_shared之前的原则是既然释放资源delete由智能指针负责,那么应该把new封装起来,否则会让人觉得自己调用了new,但没有调用delete,似乎与谁申请,谁释放的原则不符。C++也沿用了这一做法。

2、随着引用对象的增加std::shared_ptr p2 = p1,指针的引用计数有1变为2,当p2退出作用域后,p1的引用计数变回1,当main函数退出后,p1离开main函数的作用域,此时p1被销毁,当p1销毁时,检测到引用计数已经为1,就会在p1的析构函数中调用delete之前std::make_shared创建的指针。

下面展示shared_ptr相互引用会导致什么样的后果 ?

先看示例代码:

class TestB;
class TestA
{
public:
TestA()
{
std::cout << "TestA()" << std::endl;
}
void ReferTestB(std::shared_ptr<TestB> test_ptr)
{
m_TestB_Ptr = test_ptr;
}
~TestA()
{
std::cout << "~TestA()" << std::endl;
}
private:
std::shared_ptr<TestB> m_TestB_Ptr; //TestB的智能指针
};

class TestB
{
public:
TestB()
{
std::cout << "TestB()" << std::endl;
}

void ReferTestB(std::shared_ptr<TestA> test_ptr)
{
m_TestA_Ptr = test_ptr;
}
~TestB()
{
std::cout << "~TestB()" << std::endl;
}
std::shared_ptr<TestA> m_TestA_Ptr; //TestA的智能指针
};

int main(int argc, char** argv)
{
shared_ptr<TestA> ptr_a = make_shared<TestA>();
shared_ptr<TestB> ptr_b = make_shared<TestB>();
ptr_a->ReferTestB(ptr_b);
ptr_b->ReferTestB(ptr_a);

return 0;
}


运行结果如下:



只看到TestA和TestB的构造函数被调用了,没看到析构函数被调用。在main即将退出之时,ptr_a和ptr_b的引用计数都是2,因此析构函数不会被调用。智能指针ptr_a中引用了ptr_b,同样ptr_b中也引用了ptr_a,在main函数退出前,ptr_a和ptr_b的引用计数均为2,退出main函数后,引用计数均变为1,也就是相互引用。

ptr_a对ptr_b说,哎,我说ptr_b,我现在的条件是,你先释放我,我才能释放你,这是天生的,造物者决定的,改不了。ptr_b也对ptr_a说,我的条件也是一样,你先释放我,我才能释放你,怎么办?是吧,大家都没错,相互引用导致的问题就是释放条件的冲突,最终也可能导致内存泄漏。该怎么办呢 ?

此时weak_ptr就该上场了。weak_ptr相互引用不会导致引用计数增加。

class TestB;
class TestA
{
public:
TestA()
{
std::cout << "TestA()" << std::endl;
}
void ReferTestB(std::shared_ptr<TestB> test_ptr)
{
m_TestB_Ptr = test_ptr;
}
~TestA()
{
std::cout << "~TestA()" << std::endl;
}
private:
weak_ptr<TestB> m_TestB_Ptr; //TestB的智能指针
};

class TestB
{
public:
TestB()
{
std::cout << "TestB()" << std::endl;
}

void ReferTestB(std::shared_ptr<TestA> test_ptr)
{
m_TestA_Ptr = test_ptr;
}
~TestB()
{
std::cout << "~TestB()" << std::endl;
}
weak_ptr<TestA> m_TestA_Ptr; //TestA的智能指针
};


把成员变量改成weak_ptr类型的,main函数不用变,那让我们再看看运行结果。



由以上代码运行结果我们可以看到:

 1、所有的对象最后都能正常释放,不会存在上一个例子中的内存没有释放的问题。

 2、ptr_a 和ptr_b在main函数中退出前,引用计数均为1,也就是说,在TestA和TestB中对std::weak_ptr的相互引用,不会导致计数的增加。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息