C++智能指针
C++智能指针
智能指针与异常
如果使用智能指针, 如果程序块过早的结束, 智能指针能保证在内存不再需要时进行释放. (特别是在有多个出口的函数中 -- 虽然应尽量避免设计这样的函数, 但凡事总有例外 -- 此时使用智能指针来自动释放内存是非常方便的)
对于异常处理来说, 考虑下面两个函数, 当程序发生异常时, 智能指针也能正确的释放内存.
void f3()
{
int* p = new int(10);
throw "some error infomations";
delete p;
}
void f4()
{
std::shared_ptr<int> sp = std::make_shared<int>(10);
throw "some error infomations";
}
使用智能指针时的注意事项
C++智能指针使用时需要注意的事项, C++11中已经废弃了 auto_ptr, 因此不再讨论其用法, 无特殊说明, 下面的事项对 auto_ptr 而言, 可能是不正确的.
(1) 不要把一个原生指针给多个智能指针对象管理, 对所有的智能指针对象都成立
int* p = new int(2);
std::shared_ptr<int> sp0(p);
std::shared_ptr<int> sp1(p); // 错误, 不能将同一原始指针对象给两个智能指针对象管理
(2) 不要把 this 指针给智能指针对象, 对所有的智能指针对象(包括 auto_ptr)都成立, 下面的代码演示错误的使用方法
#define PRINT_FUN() printf("%s:%d\n", __FUNCTION__, __LINE__) class CTest{ public: CTest(){}; ~CTest(){ PRINT_FUN(); }; void Run() { m_sp = std::shared_ptr<CTest>(this); // 错误, 当 CTest 对象释放时也会释放 m_sp , 此时会再次 delete CTest 对象. (析构函数中的打印消息可以看出会出现一个对象两次调用析构函数.) } private: std::shared_ptr<CTest> m_sp; }; std::shared_ptr<CTest> sp(new CTest()); sp->Run(); 或者这样写 CTest t; t.Run();
(3) 不要在函数实参里创建智能指针对象
function ( shared_ptr<int>(new int), g( ) ); //有缺陷
可能的过程是先 new int, 然后调 g( ), g( )发生异常, shared_ptr<int> 没有创建, int内存泄露
推荐写法:
shared_ptr<int> p(new int());
f(p, g());
(4) 处理不是 new 创建的对象要小心. 如果确实需要这样做, 需要智能指针传递一个删除器, 自定义删除行为.
int* pi = (int*) malloc(4);
shared_ptr<int> sp(pi); // shared_ptr 析构时将调用 delete. 使用 malloc 分配内存, 用 delete 释放显然不对.
(5) 不要使用 new 创建一个智能指针对象.如 new shared_ptr<T> : 本来 shared_ptr 就是为了管理指针资源的, 不要又引入一个需要管理的指针资源shared_ptr<T>*
(6) 使用 dynamic_pointer_cast 进行转换(C++11 中已废弃 shared_dynamic_cast)
class B { public: B(){}; virtual ~B(){}; }; class D : public B { public: D(){}; virtual ~D(){}; }; std::shared_ptr<B> sp(new D); B* b = sp.get(); D* d = dynamic_cast<D*>(b); 正确用法: std::shared_ptr<B> spb(new D); std::shared_ptr<D> spd = std::dynamic_pointer_cast<D>(spb);
(7) 不要 memcpy 智能指针对象
shared_ptr<B> sp1(new B);
shared_ptr<B> sp2;
memcpy(&sp2, &sp1, sizeof(shared_ptr<B>)); //sp2.use_count()==1
很显然,不是通过正常途径(拷贝构造,赋值运算),引用计数是不会正确增长的。
(8) 智能指针对象数组的使用, 需要自定义释放器.
shared_ptr 数组, std::shared_ptr<A> p(new A[10], std::default_delete<A[]>());
std::unique_ptr<int[]>(new int[10], std::default_delete<int[]>());
(9) 将智能指针对象作为函数参数传递时要小心, 如下面的代码, 当调用所在的表达式结束(即函数调用返回)时, 这个临时对象就被销毁了, 它所指向的内存也被释放.
int* pa = new int(10); // 小心, 不是一个智能指针
f(std::shared_ptr<int>(pa)); // 合法的, 但内存会被释放
int a = *pa; // 错误, pa已经被释放, 但继续指向已经释放的内存, 从而变成了一个空悬指针, 现在试图访问 pa 的值, 其结果是未定义的
应该这样使用:
std::shared_ptr<int> sp(new int(10));
f(std::shared_ptr<int>(sp)); // 调用拷贝构造函数, sp.use_count == 2
(10) 当将一个智能指针对象(如 shared_ptr)绑定到一个普通指针时, 就将内存管理的责任交给了这个 shared_ptr. 此后就不应该使用内置指针来访问 shared_ptr 所指向的内存了.
(11) 不能使用 delete 释放 get 返回的普通指针. get 函数的设计是为了向不能使用智能指针的代码传递一个普通指针, 应该减少 get 函数的调用.
(12) 不要使用 get 返回的普通指针来初始化另一个智能指针, 或为另一个智能指针赋值. 显然如果这样做, 将导致两次释放相同的内存, 或者其中一个已经将内存释放, 但另一个还在使用.
std::shared_ptr<int> sp = std::make_shared<int>(10);
int* p = sp.get();
{
std::shared_ptr<int> sp(p);
}
int x = *sp;