C++学习(六)智能指针

目录

介绍

分类

(1)auto_ptr

(2)unique_ptr

(3)shared_ptr

(4)weak_ptr


介绍

        在C++中,动态内存的管理是通过new和delete运算符来完成的,动态内存管理容易出现两个问题:一个是忘记释放内存,会导致内存泄露;一个是在还有指针引用的情况下就释放内存,会导致产生引用非法内存的指针。

        而智能指针就是用来帮我们管理动态分配的内存,能够避免内存泄露。智能指针本质上是一个类模板,通过重载运算符*、->,使智能指针能够和普通指针一样使用。智能指针不是指针,是一个管理指针的类,用来存储指向动态分配对象的指针,负责自动释放动态分配的对象,防止堆内存泄漏;动态分配的资源,交给一个类对象去管理,当类对象声明周期结束时,自动调用析构函数释放资源。

        智能指针都定义在memory头文件中。

分类

智能指针不是线程安全的,使用方法:get()、swap()、unique()、use_count()、reset()、release()、make_shared(模板方法)

unique_ptr在默认情况下和裸指针的大小是一样的,不会造成额外的内存开销;shared_ptr是裸指针的两倍,因为除了要管理一个裸指针外,还要维护一个引用计数。

(1)auto_ptr

已经被弃用了,被unique_ptr所取代。

被弃用原因:

(1)复制或者赋值都会改变资源的所有权。

int main()
{
	auto_ptr<string> p(new string("11"));
	auto_ptr<string> p1(new string("22"));
	cout << "p:" << p.get() << endl;
	cout << "p1:" << p1.get() << endl;

    // p1赋值给p后,p会先将自己原先托管的指针释放掉,然后接收p1所托管的指针,
    // p1所托管的指针置空,也就是p托管了p1托管的指针,而p1放弃了托管。
	p = p1;

	cout << "赋值后 p:" << p.get() << endl;
	cout << "赋值后 p1:" << p1.get() << endl;

}

(2)在STL容器中使用会有重大风险,因为容器内的元素必须支持可复制和可赋值。

int main()
{
	vector<auto_ptr<string>> vec;
	auto_ptr<string> p(new string("11"));
	auto_ptr<string> p1(new string("22"));

	vec.push_back(move(p));
	vec.push_back(move(p1));

	cout << "vec[0]: " << *vec[0] << endl;
	cout << "vec[1]: " << *vec[1] << endl;

	// 如果进行赋值,vec[1]的元素就变成空值,会出现访问越界问题
	vec[0] = vec[1];
	cout << "赋值后:vec[0]: " << *vec[0] << endl;
	cout << "赋值后:vec[1]: " << *vec[1] << endl;

}

(3)不支持对象数组的内存管理

	
//不能这样定义
auto_ptr<int[]> array(new int[2]);

 

(2)unique_ptr

unique_ptr将拷贝构造函数和赋值重载函数都禁用掉了,所以不能进行拷贝和复制。C++11用unique_ptr取代了auto_ptr,两者用法几乎一样。

unique_ptr特性:

(1)排他所有权模式:两个指针不能指向同一个资源。

(2)无法进行左值赋值构造和左值赋值操作,但允许临时右值赋值构造和赋值。

int main()
{

	unique_ptr<int> p(new int(1));//	unique_ptr<int> p1 = make_unique<int>();  这两种方式是一样的
	unique_ptr<int> p1(new int(2));
	cout << "P:" << p.get() << endl;
	cout << "P1:" << p1.get() << endl;

	p = p1;//错误,unique_ptr不能进行拷贝
	unique_ptr<int> p2(p); //错误,unique_ptr不能进行拷贝

	p = move(p1);//使用move将左值转成右值就可以进行赋值了,效果和auto_ptr一样

	cout << "赋值后 P:" << p.get() << endl;
	cout << "赋值后 P1:" << p1.get() << endl;


	system("pause");
}

(3)在容器中保存指针是安全的。

int main()
{
    //可以这样定义,会自动调用delete[]函数释放内存
    unique_ptr<int[]> array(new int[2]);


	vector<unique_ptr<string>> vec;
	unique_ptr<string> p(new string("11"));
	unique_ptr<string> p1(new string("22"));

	vec.push_back(move(p));
	vec.push_back(move(p1));

	cout << "vec[0]: " << *vec[0] << endl;
	cout << "vec[1]: " << *vec[1] << endl;

	// 如果进行赋值,vec[1]的元素就变成空值,会出现访问越界问题

	vec[0] = vec[1];//错误,不允许直接赋值
	vec[0] = move(vec[1]); //如果一定要赋值,使用move修饰

	cout << "赋值后:vec[0]: " << *vec[0] << endl;
	cout << "赋值后:vec[1]: " << *vec[1] << endl;

}

(3)shared_ptr

shared_ptr允许多个智能指针可以指向同一块资源,并且能够保证共享的资源只会被释放一次。shared_ptr采用的是引用计数原理来实现多个shared_ptr对象之间共享资源,当有一个shared_ptr对象引用这个资源时,引用计数加1,当一个shared_ptr对象被销毁时,会调用析构函数,引用计数减1,当引用计数为0时,这个资源就会被自动释放掉。

实例:输出结果都是1

int main()
{

	shared_ptr<int> p(new int(1)); 
	cout << *p << endl;

	shared_ptr<int> p1 = p;
	cout << *p1 << endl;

	shared_ptr<int> p2 = make_shared<int>(2);
	p2 = p;
	cout << *p2 << endl;

	system("pause");
}

shared_ptr会出现循环引用的问题:

  •  use_count(): 返回智能指针对象的引用计数。

实例:输出结果都是2;

struct A
{
	shared_ptr<A> a;
	shared_ptr<A> b;
};


int main()
{

	shared_ptr<A> aa(new A);
	shared_ptr<A> bb(new A);
	aa->b = bb;
	bb->a = aa;

    // use_count(): 返回智能指针对象的引用计数。
	cout << "aa的引用计数" << aa.use_count() << endl;
	cout << "bb的引用计数" << bb.use_count() << endl;

	system("pause");
}

原因:

(1)这是因为当创建出aa和bb时,引用计数都是1。

(2)当aa的b指向bb所指向的资源时,bb的引用计数就+1,bb的a指向aa所指向的资源时,aa的引用计数+1,都变成2。

(3)当这两个智能指针使用完后,调用析构函数,引用计数都-1,都变成1,由于引用计数不为0,所以aa和bb所指向的对象不会被释放。

(4)当aa所指向的资源释放需要当bb中的a被销毁,就需要bb资源的释放,bb所指向的资源释放就需要当aa中的b被销毁,就需要aa资源的释放。因此aa和bb都有对方的“把柄”,这两个就造成循环引用现象,最终这aa和bb资源就不会进行释放。 

因此就需要通过weak_ptr来解决。

(4)weak_ptr

weak_ptr的对象可以指向shared_ptr,并且不会改变shared_ptr的引用计数,一旦最后一个shared_ptr被销毁时,对象就会被释放。

实例:输出结果都是1,

struct A
{
	weak_ptr<A> a;
	weak_ptr<A> b;
};


int main()
{

	shared_ptr<A> aa(new A);
	shared_ptr<A> bb(new A);
	aa->b = bb;
	bb->a = aa;

    // use_count(): 返回智能指针对象的引用计数。
	cout << "aa的引用计数" << aa.use_count() << endl;
	cout << "bb的引用计数" << bb.use_count() << endl;

	system("pause");
}

因此在双向链表等有多个指针的时候,结构体内的指针应该定义为weak_ptr,避免出现循环引用。

weak_ptr可以通过expired()函数判断引用计数是否为0,返回true说明对象已经被销毁了。用lock()函数可以将weak_ptr转换为shared_ptr。

  • 9
    点赞
  • 26
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值