目录
自从c++11引入智能指针shared_ptr后,我们似乎再也不用担心new的内存没有释放之类的问题了,但是也带来了其他的问题。
shared_ptr
智能指针是指向动态分配(堆)对象的指针,用于生存期控制,能确保自动正确的销毁动态分配的对象,防止内存泄露。它的一种通用实现技术是使用引用计数。每次使用它,内部引用计数加1,每析构一次,内部引用计数减1,减为0时,删去所指向的堆内存。
C++11中的智能指针包括:
std::shared_ptr
std::unique_ptr
std::weak_ptr
其中最常用的就是std::shared_ptr。每个shared_ptr的拷贝都指向相同的内存.最后一个shared_ptr析构的时候,堆中的内存才会被释放。share_ptr类型的对象具有获得指针所有权并共享所有权的能力。
初始化:
可以通过指针来初始化它:template <class U> explicit shared_ptr (U *p);
std::shared_ptr<int>p(new int(2));
std::shared_ptr<int>p2= p;
std::shared_ptr<BaseConnector> m_connt = make_shared<Connector>(m_ios, m_strIP, m_port);
通过构造函数、赋值函数或者 make_shared 函数初始化智能指针。
例子:
// shared_ptr constructor example
#include <iostream>
#include <memory>
struct C {int* data;};
int main () {
std::shared_ptr<int> p1;
std::shared_ptr<int> p2 (nullptr);
std::shared_ptr<int> p3 (new int);
std::shared_ptr<int> p4 (new int, std::default_delete<int>());
std::shared_ptr<int> p5 (new int, [](int* p){delete p;}, std::allocator<int>());
std::shared_ptr<int> p6 (p5);
std::shared_ptr<int> p7 (std::move(p6));
std::shared_ptr<int> p8 (std::unique_ptr<int>(new int));
std::shared_ptr<C> obj (new C);
std::shared_ptr<int> p9 (obj, obj->data);
std::cout << "use_count:\n";
std::cout << "p1: " << p1.use_count() << '\n';
std::cout << "p2: " << p2.use_count() << '\n';
std::cout << "p3: " << p3.use_count() << '\n';
std::cout << "p4: " << p4.use_count() << '\n';
std::cout << "p5: " << p5.use_count() << '\n';
std::cout << "p6: " << p6.use_count() << '\n';
std::cout << "p7: " << p7.use_count() << '\n';
std::cout << "p8: " << p8.use_count() << '\n';
std::cout << "p9: " << p9.use_count() << '\n';
return 0;
}
2. 智能指针中的原始指针,通过 get 获取
char* pData = pBuf.get();
3.注意事项。智能指针虽然能自动管理堆内存,但他有不少陷进,使用时要注意:
1.不要把一个原生指针给多个shared_ptr管理
int* ptr = new int;
shared_ptr<int> p1(ptr);
shared_ptr<int> p2(ptr); //logic error
导致ptr被删除两次
2.不要把this指针赋给shared_ptr
3. 不要在函数实参里创建shared_ptr
function( share_ptr<int>(new int), g( ) );//有缺陷
可能的过程是先new int,然后调g( ),g( )发生异常,shared_ptr<int>没有创建,int内存泄露
shared_ptr<int> p(new int());
f(p, g());
5.shared_ptr作为对象的成员时,小心因循环引用造成无法释放资源。
struct A;
struct B;
struct A
{
std::shared_ptr<B> m_b;
};
struct B
{
std::shared_ptr<A> m_a;
};
std::shared_ptr<A> ptrA(new A);
std::shared_ptr<B> ptrB(new B);
ptrA->m_b = ptrB;
ptrB->m_a = ptrA;
ptrA和ptrB相互引用,离开作用域时引用计数都为1,导致内存没有被释放,解决办法是把A和B任何一个的成员变量改为weak_ptr
解决办法是把A和B任何一个的成员变量改为weak_ptr
struct B
{
std::weak_ptr<A> m_a;
};
ptrB->m_a不会增加A对象的引用计数,因此A对象离开作用域时,引用计数为0,m_b的引用计数减一,b离开作用域后引用计数由1减为0.
std::weak_ptr
弱引用指针,用来监视智能指针,不会使应用计数加1.在访问所引用对象前必须先转换成std::shared.
用来管理临时所有权的概念:当某个对象只有存在时才需要被访问,而且随时都可能被他人删去,可以使用。
来跟踪该对象。需要获得临时所有权时,则将其转换为 std::shared_ptr,此时如果原来的std::shared_ptr 被销毁,则该对象的生命期将被延长至这个临时的 std::shared_ptr 同样被销毁为止。
std::unique_ptr
unique_ptr不会共享它的指针。 无法将它复制到另一个unique_ptr, unique_ptr只能移动。 这意味着内存资源的所有权将转移到新的unique_ptr和原始unique_ptr不再拥有它。
int* p = new int;
std::unique_ptr<int> ptr(p);
std::unique_ptr<int> ptr1 = ptr; //不能复制,编译报错
auto ptr2 = std::move(ptr); //转移所有权, 现在ptr那块内存归ptr2所有, ptr成为无效的指针.
使用时注意事项:
1.常规指针转换为智能指针:
①.new的普通指针与shared_ptr转换:
如图所示,这会发生什么情况?答案是输出的会是随机数,因为经过func函数后,我们用p初始化的临时智能指针已经被析构了,引用计数先+1,后-1。所以经过func函数后,
p指向的对象被释放,再解引用自然无法得到我们想要的结果。
#include<iostream>
#include <memory>
using namespace std;
void func(shared_ptr<int>)
{
;
}
int main()
{
int a = 5;
auto p = new int(5);
func(shared_ptr<int>(p));
cout << *p << endl;
return 0;
}
这种情况下,正确的做法如图所示:一开始就使用智能指针。
#include<iostream>
#include <memory>
using namespace std;
void func(shared_ptr<int>)
{
;
}
int main()
{
//int a = 5;
auto p = make_shared<int>(5);
func(shared_ptr<int>(p));
cout << *p << endl;
return 0;
}
②.指向栈的指针与shared_ptr转换:
如图所示,这种情况,程序会直接崩溃,因为智能指针试图释放保存在栈上的变量,它越界了
#include<iostream>
#include <memory>
using namespace std;
void func(shared_ptr<int>)
{
;
}
int main()
{
int a = 5;
auto p = &a;
func(shared_ptr<int>(p));
cout << *p << endl;
return 0;
}
3.智能指针向常规指针的转换
我们通常使用get()函数向智能指针索要所指向对象的拥有权,但是这样有时也会造成错误:
auto p = make_shared<int>(42);
int* iPtr = p.get();
{
shared_ptr<int>(iPtr);
}
int value = *p; // Error! 内存已经被释放
p与iPtr指向了相同的内存,然而通过get方法后,将内存管理权转移给了普通指针。iPtr传递给里面程序块的临时智能指针后,引用计数为1,随后出了作用域,减少为0,释放内存。