C++11新特性——C++智能指针(一)

C++11新特性——C++智能指针(一)

​ 如果我们分配的动态内存都交由生命周期的对象来处理,那么对象过期时,让它的析构函数删除指向的内存,这看似是一个very nice的方案?

智能指针就是通过这个原理来解决指针自动释放的问题!

  • C++98提供了auto_ptr 模板的解决方案
  • C++11增加unique_ptrshared_ptrweak_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;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值