
学c++的人都知道,在c++里面有一个痛点,就是动态内存的管理,就我所经历的少量问题来看,很多莫名其妙的问题,最后都发现是内存管理不当引起的。
但像java等其余少量语言则不会有这样的问题,为什么呢,由于它们有很好的解决内存的方法,比方java的垃圾回收机制,现在,我们c++终于也有了智能指针。
简单地说,智能指针是用对象去管理一个资源指针,同时用一个计数器计算引用当前指针对象的个数,当管理指针的对象添加或者减少时,计数器也相应加1或者减1,当最后一个指针管理对象销毁时,计数器为1,此时在销毁指针管理对象的同时,也对指针管理对象所管理的指针进行delete操作。
下面我们详情两个常用的智能指针std::shared_ptr和std::weak_ptr。
std::shared_ptr包装了new操作符动态分配的内存,可以自由拷贝复制,基本上是使用最多的一个智能指针类型。
下面是一个代码例子:
#include <memory>#include <iostream>class Test{public: Test() { std::cout << "Test()" << std::endl; } ~Test() { std::cout << "~Test()" << std::endl; }};int main(){ std::shared_ptr<Test> p1 = std::make_shared<Test>(); std::cout << "1 ref:" << p1.use_count() << std::endl; { std::shared_ptr<Test> p2 = p1; std::cout << "2 ref:" << p1.use_count() << std::endl; } std::cout << "3 ref:" << p1.use_count() << std::endl; return 0;}结果如下:
Test()1 ref:12 ref:23 ref:1~Test()针对代码解读如下:
std::weak_ptr有什么特点呢?与std::shared_ptr最大的差别是在赋值的时候,不会引起智能指针计数添加。
weak_ptr被设计为与shared_ptr共同工作,可以从一个shared_ptr或者者另一个weak_ptr对象构造,取得资源的观测权。但weak_ptr没有共享资源,它的构造不会引起指针引用计数的添加。同样,在weak_ptr析构时也不会导致引用计数的减少,它只是一个静静地观察者。weak_ptr没有重载operator*和->,这是特意的,由于它不共享指针,不能操作资源,这是它弱的起因。如要操作资源,则必需使用一个非常重要的成员函数lock()从被观测的shared_ptr取得一个可用的shared_ptr对象,从而操作资源。
代码如下:
#include <memory>#include <iostream>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(){ std::shared_ptr<TestA> ptr_a = std::make_shared<TestA>(); std::shared_ptr<TestB> ptr_b = std::make_shared<TestB>(); ptr_a->ReferTestB(ptr_b); ptr_b->ReferTestB(ptr_a); return 0;}运行结果:
TestA()TestB()可以看到,上面代码中,我们创立了一个TestA和一个TestB的对象,但在整个main函数都运行完后,都没看到两个对象被析构,这是为什么呢?
原来,智能指针ptr_a中引用了ptr_b,同样ptr_b中也引用了ptr_a,在main函数退出前,ptr_a和ptr_b的引用计数均为2,退出main函数后,引用计数均变为1,也就是相互引用。
这等效于说:
是吧,大家都没错,相互引用导致的问题就是释放条件的冲突,最终也可能导致内存泄漏。
我们在上面的代码基础上使用std::weak_ptr进行修改,如下:
#include <iostream>#include <memory>class TestB;class TestA{public: TestA() { std::cout << "TestA()" << std::endl; } void ReferTestB(std::shared_ptr<TestB> test_ptr) { m_TestB_Ptr = test_ptr; } void TestWork() { std::cout << "~TestA::TestWork()" << std::endl; } ~TestA() { std::cout << "~TestA()" << std::endl; }private: std::weak_ptr<TestB> m_TestB_Ptr;};class TestB{public: TestB() { std::cout << "TestB()" << std::endl; } void ReferTestB(std::shared_ptr<TestA> test_ptr) { m_TestA_Ptr = test_ptr; } void TestWork() { std::cout << "~TestB::TestWork()" << std::endl; } ~TestB() { ////把std::weak_ptr类型转换成std::shared_ptr类型 std::shared_ptr<TestA> tmp = m_TestA_Ptr.lock(); tmp->TestWork(); std::cout << "2 ref a:" << tmp.use_count() << std::endl; std::cout << "~TestB()" << std::endl; } std::weak_ptr<TestA> m_TestA_Ptr;};int main(){ std::shared_ptr<TestA> ptr_a = std::make_shared<TestA>(); std::shared_ptr<TestB> ptr_b = std::make_shared<TestB>(); ptr_a->ReferTestB(ptr_b); ptr_b->ReferTestB(ptr_a); std::cout << "1 ref a:" << ptr_a.use_count() << std::endl; std::cout << "1 ref b:" << ptr_a.use_count() << std::endl; return 0;}运行结果:
TestA()TestB()1 ref a:11 ref b:1~TestA::TestWork()2 ref a:2~TestB()~TestA()由以上代码运行结果我们可以看到:
weak_ptr<T> w; //空weak_ptr可以指向类型为T的对象weak_ptr<T> w(shared_ptr sp); //与sp指向相同对象的weak_ptr, T必需能转换为sp指向的类型w = p; //p可以是shared_ptr或者者weak_ptr,赋值后w和p共享对象w.reset(); //weak_ptr置为空w.use_count(); //与w共享对象的shared_ptr的计数w.expired(); //w.use_count()为0则返回true,否则返回falsew.lock(); //w.expired()为true,返回空的shared_ptr;否则返回指向w的shared_ptruniqut_ptr是一种对资源具备排他性拥有权的智能指针,即一个对象资源只能同时被一个unique_ptr指向。
T *pT = new T();std::unique_ptr<T> up1(pT);auto pT = make_unique<T>();//up也是一个std::unique_ptr<T>指针unique_ptr<T> up1 = std::move(up); unique_ptr<T> up(new T()); //okunique_ptr<T> up1(up); //error, can not be copyunique_ptr<T> up2 = up; //error, can not be assignedunique_ptr<T> pT(new T());unique_ptr<T> pT2 = std::move(pT); //移动赋值,此时pT被销毁,为空unique_ptr<T> pT3(std::move(pt2)); //移动拷贝,此时pT2被销毁,为空unique_ptr<T> GetPtr(); //function getthe unique pointerunique_ptr<T> pT = GetPtr(); // ok#include <iostream>int main(){ std::unique_ptr<int> pInt; pInt.reset(new int()); int *p = pInt.release(); //释放所有权 //因为unique_ptr有std::unique_ptr<T[]>的重载函数,所以它可以用来管理数组资源 std::unique_ptr<int[]> pArray(new int[3]{1,3,3}); }可以看出,智能指针其实是std::shared_ptr和std::unique_ptr, std::shared_ptr可以有多个引用对象,但不能互相引用,而std::unique_ptr只能有一个引用,不能赋值或者者拷贝,但可以移动赋值和移动拷贝,std::weak_ptr实际上是对std::shared_ptr的补充,它并不能对对象进行具体的操作。
注意:shared_ptr,weak_ptr,unique_ptr这些都是模板,并不是指针或者者其余的。