c++智能指针详解

5 篇文章 0 订阅
3 篇文章 0 订阅

指针一直都是c\c++头疼的东西,虽然方便,但是能不用动态内存分配就不用,因为你new 多了后在释放后程序中就会有很多碎片,而且释放已经引用会使用不规范会导致程序崩溃
C++后面其实有很多东西都是从很老版本的boost库引入进来了,比如functional
智能指针等等 智能指针就是采用了RAII思想下期再讲
因为raw指针经常会导致内存泄漏,非法内存访问,多次释放同一块内存,释放非法动态内存等问题
C++通过模板提供了更加方便的对raw指针包装的智能指针,作为对象 他使用上类似raw指针
可以避免raw指针的问题 智能指针不需要显示调用delete 他会自动释放不在使用的内存
智能指针有三种:shared_ptr,unique_ptr,weak_ptr,他们在头文件中
他们也在标准std中

1.独享指针
*即一个T 类型的指针变量可以存储动态内存地址,也可以存储一个程序块的普通变量地址
这种指针称为 raw指针(原始指针),因为除了内存地址外他没有存储别的东西
raw指针指向的动态分配内存应该及时释放,并且对new分配的内存要用delete释放
对于new[] 分配的内存要用delete[]释放,如果一个指针变量在指向新的内存时
没有释放原来的动态内存,就会造成内存泄漏
如果多次释放同一块动态内存也会引起同样的问题而导致程序崩溃
假设有2个自主指向同一块内存,如果通过一个指针释放了这块内存空间
但又通过另外一个指针访问这个块内存空间,也会引起非法内存访问的严重问题

template<typename T>
unique_ptr<T>MOVE() {
	auto p = unique_ptr<T>{ new T };
	return p;
}

void raw() {
	auto* p = new int;
	auto* q{ new int[3] };
	auto i{ 3 };
	p = &i;
	//delete p; // 错 p指向的不是动态内存 因为p之前指向了i,而i不是动态内存,导致delete p也会导致程序崩溃
	//delete q; // 错 只是释放了q指向的3个int内存块中的第一个int内存块,导致另外2个内存块无法释放,也会内存泄漏
	delete[] q; // 这样就释放了所以的内存块 如果用错了delete 也会造成内存汇漏
	p = new int; // 重新分配p
	q = p; //将q指向p
	delete p; // 删除p
	p = 0; // 因为p,q指向同一个内存然后delete p释放了这块内存,但是q不知道,继续用q获取这块内存就导致非法内存访问
	auto j{ *q };
}
void Unique_ptr() { // unique_ptr<>对象是对raw指针的包裹,将一个T *类型的动态分配内存块的raw指针
//作为unique_ptr<T>构造函数的参数就可以创建一个对象,在它退出作用于时销毁
//析构函数会自动释放其包含的raw指针的动态内存 他是一个独享指针不能复制拷贝给其他同对象但是支持移动语句
	unique_ptr<double>p{ new double{0.} };
	*p = 3.14; // 和指针一样*解引用,->,p内存
	cout << *p << "\t";
	double* pb = p.get(); // p是对象可以用.来访问成员
	*pb = 3.1415;
	cout << *p << "\t"; 
	cout << *p.get() << "\t";
	p.reset(new double); // 将一个新的 raw指针传递给他,原来的raw指针指向的内存被自动释放
	cout << *p.get() << "\t"<< *pb<<endl; // pb也被释放了因为指向的是同一块内存 释放后地址就不同了
	p.reset();//没丢参数就释放raw指针内存然后设为nullptr指针
	cout << p.get()<<"\t";
	p.reset(new double);
	double* rawp = p.release(); // release()函数返回raw指针并将raw指针设置为空指针,并将p内部的raw指针设为空
	*rawp = 3.0; // 然后重新指向
	cout << p.get() << "\t"<<*rawp<<endl;

	unique_ptr<char[]>ch{ new char[5] };
	char* c = &ch[0];
	ch[0] = 'a', ch[1] = 'b', ch[2] = 'c';
	for (int i = 0; i != 3; i++) {
		cout << ch[i] << " "<<*c++<<ends; // 当然和raw指针一样但是不能用*ch++来偏移但是可以用原始指针偏移
	}// 如果直接打印ch就把整个数组打印出来了
	cout << endl;

	//unique_ptr<int[]>in{ new int[3] };
	//unique_ptr<int[]>os{ new int[3] };
	//in[0] = 0, in[1] = 1, in[2] = 2;
	os = in; 错误
	//os[0] = in[0]; // 元素可以复制移动,但是对象就不能了 因为他是独享指针 原始指针可以用
	unique_ptr<int>n{ new int(3) };
	unique_ptr<int>m{ new int };
	//m = n; 但是对象就不行了 m=&n都不行
	*m = *n;
	cout << *m << " " << *n << endl;	
	//auto move = n; 会报错 但是建议用移动语句
	auto move = MOVE<int>(); // 所以可以用返回的对象将其赋予
	auto move1 = get_unique();
	auto move2 = std::move(move); // 就是需要用右值来给unique_ptr来赋予对象
}
unique_ptr<int>get_unique() { // 移动语句
	auto p = unique_ptr<int>{ new int{2} };
	return p;
}

void Make_unique() { // make_unique<>()可以很方便的帮助创造一个unique_ptr<>指针并动态分配
	auto p = make_unique<int>(5); // 这样只是 分配了1个int动态内存吧3赋给他
	cout << *p.get() << endl;
	p.reset();
	auto q = make_unique<int[]>(3);
	for (int i = 0; i != 3; i++) {
		q[i] = i;
		cout << q[i] << ends; // *q.get是0 0 0因为没偏移
	}
}
int main() {
	Unique_ptr();
	Make_unique();
	while (true);
	return 0;
}

2.共享指针
shared_ptr<>和unique_ptr<>一样也是对raw指针的包装,并可以以类似的使用但他是可以通过
拷贝构造函数或赋值运算符的 任意赋值这些赋值对象将共享同一个raw指针,即同一块地址
所以它被称为 共享指针 因为多个shared_ptr 共享同一个raw指针 所以内部维护了一个引用计数器 计指针个数
当shared_ptr 指针内部的这个引用计数器变为0时析构函数才是真正释放这块内存 如果不是0析构函数仅是将计数器减少1

#include <iostream>
#include <memory>
#include <string>
using namespace std;
int main() {
	auto ptr = make_shared<string>("hello");
	shared_ptr<int[]>n{ new int[3] };
	cout << *ptr << "\t" << ptr.use_count() << endl; // 当前内存块引用计数为1
	auto p = ptr;
	*p += " world";
	cout << *ptr << "\t" << ptr.use_count() << endl;
	cout << *p << "\t" << p.use_count() << endl;
	ptr.reset(); //ptr设置为一个空指针 use_count -1 同样也可以释放在重新分配地址
	//cout << *ptr << "\t" << ptr.use_count() << endl; 再次调用就会报错 因为*没有元素
	cout << ptr << "\t" << ptr.use_count() << endl; // 地址是0
	cout << *p << "\t" << p.use_count() << endl;
	while(true);
	return 0;
}
// shared_ptr<> 的引用计数是原子操作,因此是线程安全的,对于共享同一个对象的多线程,可以为每
// 一个线程定义一个shared_ptr<>,这些shared_ptr<>共享同一个对象就可以使每个线程都能访问这个对象

3.弱指针
weak_ptr<>称弱指针 是一个配合shared_ptr指针的智能指针 shaered_ptr是一种具有
所有权的指针,每个shared_ptr都拥有它指向的对象的所有权 这个权是通过引用计数实现
weak_ptr<>只能从一个shared_ptr<>创建,他是对由共享指针管理的对象的非拥有(弱)引用
即weak_ptr<>不拥有共享指针的管理对象,但是可以用弱指针查询共享指针 管理对象
所有weak_ptr<>是shared_ptr<>的观察者 观察对象是否被销毁,如果没销毁者weak_ptr可以
转换成shared_ptr<>而承担起临时所有权,如果销毁了元素的共享指针则会延迟对象生命周期

#include <iostream>
#include <memory>
using namespace std;
weak_ptr<int>gw;
void observe() {
	cout << "use_count == " << gw.use_count() << ":";
	if (auto spt = gw.lock())
		cout << *spt << endl;
	else
		cout << "gw is epired\n";
}
int main() {
	{
	auto sp = make_shared<int>(42);
	gw = sp;
	observe(); //1:42
	} //出了这个区域就被释放了
	observe();//0:is
	while (true);
	return 0;
}
  • 2
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值