C++11新特性——C++智能指针(一)
如果我们分配的动态内存都交由生命周期的对象来处理,那么对象过期时,让它的析构函数删除指向的内存,这看似是一个very nice的方案?
智能指针
就是通过这个原理来解决指针自动释放的问题!
- C++98提供了
auto_ptr
模板的解决方案- C++11增加
unique_ptr
、shared_ptr
、weak_ptr
1、auto_ptr
auto_ptr
是c++98定义的智能指针模板,其定义了管理指针的对象,可以将new获得(直接或间接)的地址赋值给这种对象。当对象过期时,其析构函数将使用delete来释放内存!c++11之后,
auto_ptr
已被unique_ptr
替代
#include <iostream>
#include <string>
#include <exception>
//用法:
#include <memory> //头文件
//auto_ptr<类型> 变量名(new 类型)
using namespace std;
忠告一:不要定义为全局变量
//auto_ptr<Test>
忠告二:不要定义指向智能指针对象的指针变量
//auto_ptr<Test> *tp = new auto_ptr<Test>(new Test());
在使用智能指针访问对象时,使用方式和普通指针一样
//auto_ptr<Test> t1; t1 = t;
class Test {
public:
Test() {
cout << "Test is construct" << endl;
debug = 1;
}
~Test() { cout << "Test is destruct" << endl; }
int getDebug(){
return debug;
}
private:
int debug;
};
void memory_leak_demo1() {
//Test* t = new Test();//内存泄露,不会调用析构函数
auto_ptr<Test> t(new Test());
//使用智能指针访问对象时,使用方式和普通指针一样
cout << "-> debug: " << t->getDebug() << endl;
cout << "* debug: " << (*t).getDebug() << endl;
//Test* tmp = t.get();
//cout << "get debug " << tmp->getDebug() << endl;
//release 取消智能指针对动态内存的托管,之前分配的内存必须手动释放
//Test* tmp = t.release();
//delete tmp;
//reset 重置智能指针托管的内存地址,如果地址不一致,原来的会被析构掉
//t.reset();
t.reset(new Test());
if(0){
Test* t1 = new Test();
t1->getDebug();
}
return;
}
void memory_leak_demo2() {
//Test* t = new Test();
auto_ptr<Test> t(new Test());//自动释放内存
{
throw exception("文件不存在");
}
//delete t;
return;
}
int main() {
memory_leak_demo1();
try {
memory_leak_demo2();
}
catch (exception e) {
cout << "catch exception: " << e.what() << endl;
}
/*
Test is construct
Test is destruct
Test is construct
Test is destruct
*/
system("pause");
return 0;
}
C++11抛弃
auto_ptr
的原因
int main() {
//弊端1、复制和赋值都会改变资源的所有权
auto_ptr<string> p1(new string("I'm martin."));
auto_ptr<string> p2(new string("I'm rock."));
printf("p1: %p\n", p1.get());
printf("p2: %p\n", p2.get());
p1 = p2;
printf("p1 = p2之后:\n");
printf("p1: %p\n", p1.get());
printf("p2: %p\n", p2.get());
/*
p1: 000001E647E67FB0
p2: 000001E647E68170
p1 = p2之后:
p1: 000001E647E68170
p2: 0000000000000000
*/
//弊端2、在stl容器中使用auto_ptr存在重大风险,因为容器内的元素必须支持
// 可复制(copy constructable)和可赋值(assignable)。
vector<auto_ptr<string> >va;
auto_ptr<string> p3(new string("I'm p3."));
auto_ptr<string> p4(new string("I'm p4."));
//左值拷贝构造也不行, 必须转成右值
va.push_back(std::move(p3));
va.push_back(std::move(p4));//右值化
cout << "va[0]: " << *va[0] << endl;
cout << "va[1]: " << *va[1] << endl;
/* va[0]: I'm p3.
va[1]: I'm p4.
*/
va[0] = va[1];//有风险
cout << "va[0]: " << *va[0] << endl;
cout << "va[1]: " << *va[1] << endl;
/*
va[0]: I'm p4.
运行终止
*/
//弊端3、不支持对象数组的内存管理
//auto_ptr<int[]> a1(new int[5]); 不能这样定义
/*
//弊端4、auto_ptr 陷阱,不能把同一段内存交给多个auto_ptr变量去管理
{
auto_ptr<string> p2;
string* str = new string("智能指针的内存管理陷阱");
p2.reset(str);
{
auto_ptr<string> p1;
p1.reset(str);
}//花括号限制了p1的生命周期
cout << "str: " << *str << endl;
//str的 内存已经被释放掉了
}
*/
return 0;
}
2、unique_ptr
//弊端1、C++11抛弃auto_ptr的原因,p1 = p2 复制和赋值都会改变资源的所有权
//unique_ptr 如何解决这个问题? 不允许显示的右值赋值和构造
unique_ptr<string> p1(new string("I'm martin."));
unique_ptr<string> p2(new string("I'm rock."));
printf("p1: %p\n", p1.get());
printf("p2: %p\n", p2.get());
//一定要转移,先使用move把左值转化成右值
p1 = std::move(p2);
printf("p1 = p2之后:\n");
printf("p1: %p\n", p1.get());
printf("p2: %p\n", p2.get());
//p1 = p2;左值赋值禁止
unique_ptr<string> p3(new string("I'm p3."));
//unique_ptr<string> p4(std::move(p3));//左值拷贝构造也不行,必须转换成右值
//弊端2、在stl容器中使用auto_ptr存在重大风险,因为容器内的元素必须支持
// 可复制(copy constructable)和可赋值(assignable)。
vector<unique_ptr<string> >vu;
unique_ptr<string> p3(new string("I'm p3."));
unique_ptr<string> p4(new string("I'm p4."));
vu.push_back(std::move(p3));
vu.push_back(std::move(p4));
cout << "vu[0]: " << *vu[0] << endl;
cout << "vu[1]: " << *vu[1] << endl;
//vu[0] = vu[1];//不支持有歧义的赋值,没有风险
cout << "vu[0]: " << *vu[0] << endl;
cout << "vu[1]: " << *vu[1] << endl;
/*
vu[0]: I'm p4.
运行终止
*/
//弊端3、auto_ptr不支持对象数组的内存管理
//unique_ptr 支持对象数组的管理
unique_ptr<int[]> ui(new int[5]);//自动会调用delete[]函数去释放
使用方法.cpp
#include <stdio.h>
#include <iostream>
#include <string>
#include <memory>
#include <vector>
using namespace std;
class Test {
public:
Test() {cout << "Test is construct" << endl;}
~Test() { cout << "Test is destruct" << endl; }
void do_something() {
cout << "do something..." << endl;
}
};
class DestructTest {
public:
void operator()(Test* pt) {
pt->do_something();
delete pt;
}
};
/*
unique_ptr<T> up ; //空的unique_ptr,可以指向类型为T的对象
unique_ptr<T> up1(new T()) ;//定义unique_ptr,同时指向类型为T的对象
unique_ptr<T[]> up ; //空的unique_ptr,可以指向类型为T[的数组对象
unique_ptr<T[]> up1(new T[]) ;//定义unique_ptr,同时指向类型为T的数组对象
unique_ptr<T,D> up(); //空的unique_ptr,接受一个D类型的删除器d,使用d释放内存
unique_ptr<T,D> up(new T()); //定义unique_ptr,同时指向类型为T的对象,接受一个D
类型的删除器d,使用删除器d来释放内存
*/
/*
unique_ptr<int> up1(new int(10));
unique_ptr<int> up2(new int(11));
up1 = std::move(up2);//必须使用移动语义,结果,up1 内存释放, up2 交由up1 管理
*/
int main() {
//构造函数
//普通的unique_ptr变量定义
unique_ptr<Test> up;
unique_ptr<Test> up1(new Test());
up1 = std::move(up1);
//数组对象的 unique_ptr 的定义
unique_ptr<Test[]> up2;
{
unique_ptr<Test[]> up3(new Test[5]); //delete []
}
//Test* t1 = new Test();
//up.reset(t1);
//使用自定义的删除器
{
unique_ptr<Test, DestructTest>up5(new Test());
}
//2、赋值,一定要使用移动语义
unique_ptr<Test> up6(new Test());
unique_ptr<Test> up7(new Test());
up6 = std::move(up7);//必须使用移动语义,结果,up6内存释放,up7交由up6管理
up6->do_something();
//可以主动释放对象
unique_ptr<Test> up8(new Test());
up8 = NULL;//up8 = nullptr; up8.reset();
printf("up8: %p\n", up8.get());
//放弃对象控制权
up8.release();//放弃对象的控制权,返回指针,将up置为空,不会释放内存
//重置
up.reset();//参数可以为空、内置指针,先将up所指对象释放,然后重置up的值
up.swap(up1);//交换
return 0;
}