由于第三方库返回的分配内存返回的指针一般是原始指针,需要手动释放内存。这时候就可以使用智能指针来自动管理分配的内存了。
下面来模拟一种使用智能指针来管理第三方库的一个情形。事实上这个例子在如MFC、OpenCV等第三方库的使用中十分常见。在下面的例子中,有2个要点值得关注:
对象的生存周期。这是导致资源(包括内存)泄漏的一个重要原因。
shared_ptr的用法。
假设有资源R,通过GetHandle()来获取其对象的句柄为我们分配所需要的资源对象Obj。
如下,在test()中创建了资源Obj数组,假如不采取任何措施,p指向的内存将发生内存泄漏。
此时我们可以用智能指针来管理内存。这里要警惕下面的错误写法。这个错误是由于shared_ptr的对象周期结束时,其管理的内存将被释放。假如采用guard2,当其调用结束时,sp的生存周期结束,p的内存释放,导致guard2之后p就成为一个野指针了。
我们可以用gurad的写法,间接的增加shared_ptr的生存周期。
但最好的是采用宏的写法,其展开后sp将与p的生存周期相同。
#include<iostream>
#include<memory>
#include<cstdlib>
class Obj {
public:
Obj() {
std::cout << "Obj" << std::endl;
}
~Obj() {
std::cout << "~Obj" << std::endl;
}
};
struct R {
void* create() {
std::cout << "enter create" << std::endl;
Obj* i = new Obj[5];
std::cout << "create" << std::endl;
return i;
}
void Release(void* p) {
std::cout << "enter Release" << std::endl;
Obj* p1 = (Obj*)p;
delete [] p1;
std::cout << "out Release" << std::endl;
}
~R() {
std::cout << "~R" << std::endl;
}
}r;
R* GetHandle() {
return &r;
}
//错误写法
//void guard2(void* p) {
// std::shared_ptr<void> sp(p, [](void* p){GetHandle()->Release(p);});
//}
//第二种方式
std::shared_ptr<void> guard(void* p) {
std::shared_ptr<void> sp(p, [](void* p){GetHandle()->Release(p);});
return sp;
}
//第二种方式
#define GUARD(P) std::shared_ptr<void> p2(p,[](void* p){GetHandle()->Release(p);});
void test() {
void* p = GetHandle() -> create();
// guard2(p);std::cout << "out gurad2" << std::endl;
// auto p2 = guard(p);
// GUARD(p);
std::cout << "out test" << std::endl;
}
int main(){
test();
int x;
std::cin >> x;
std::cout << "out main" << std::endl;
}
- 你可以通过注释或去注释来观察对象的创建和释放情况来更深一步的理解。