<一>智能指针基础
代码1
int main(){
//裸指针,手动开辟,需要自己释放,如果忘记了或者因为
//程序逻辑导致p没有释放,那么就会导致内存泄漏
int *p=new int(10);
if(***){
retur -1;
}
delete p;
return 0;
}
有没有什么办法帮我们管理指针,确保资源释放?
智能指针
利用栈上的对象出作用域时自动析构的特征,来做到资源的自动释放
问题:是否可以在堆上创建裸指针?语法上没有问题,但是我们正式希望
栈上对象出作用域能自动析构的特征来达到自动管理指针的目的,如果
将智能指针创建在堆上,那又和原来的裸指针使用遇到的问题是一样的了
需要手动delete
代码2
#include <iostream>
using namespace std;
template<typename T>
class MySmartPtr1 {
public:
MySmartPtr1(T * ptr=nullptr) : _mptr(ptr) { }
~MySmartPtr1() {
delete _mptr;
_mptr = nullptr;
}
T & operator*() { return *_mptr; }//返回的 是 & , 需要修改值
T * operator->() { return _mptr; }
private:
T * _mptr;
};
int main() {
MySmartPtr1<int> ptr(new int(10));
*ptr= 200;
return 0;
}
代码2的问题
int main() {
MySmartPtr1<int> ptr(new int(10));
//使用ptr 拷贝构造ptr2,默认的拷贝构造方式是值拷贝,所以底层
//_mptr指针 指向的是同一块内存,那么ptr2 和ptr析构的时候就会有问题了,两次析构同一片内存
MySmartPtr1<int> ptr2(ptr);
*mptr = 200;
return 0;
}
如何解决呢?
1:不带引用计数的智能指针
auto_ptr C++库提供
C++11 新标准
scoped_ptr
unique_ptr
代码 关于 auto_ptr
int main() {
auto_ptr<int> ptr1(new int(100));
auto_ptr<int> ptr2(ptr1);
*ptr2 = 200;
cout<<*ptr1<<endl;//执行报错,原因见下图
return 0;
}
现在不推荐使用auto_ptr
容器中推荐用auto_ptr吗? vector<auto_ptr> v1; v2(v1); 容器的拷贝构造和容器的赋值容易引起容器元素的拷贝构造和赋值,而auto_ptr的拷贝构造会将原来管理的底层资源(指针)置空
代码关于 scoped_ptr
int main() {
scope_ptr的处理方式
scope_ptr<int>(const scope_ptr<int> & src)=delete;//通过直接和谐掉这两个方法
scope_ptr<int> & operator=(const scope_ptr<int> & src))=delete;//通过直接和谐掉这两个方法
return 0;
}
所以scoped_ptr使用的也很少
代码关于 unique_ptr
int main() {
unique_ptr的处理方式
unique_ptr<int>(const unique_ptr<int> & src)=delete;//通过直接和谐掉这两个方法
unique_ptr<int> & operator=(const unique_ptr<int> & src))=delete;//通过直接和谐掉这两个方法
unique_ptr<int> ptr1(new int(100));
unique_ptr<int> ptr2(ptr1);//编译报错,尝试使用已经删除的函数, 要改成如下!!!
unique_ptr<int> ptr1(new int(100));
unique_ptr<int> ptr2(std::move(ptr1));//编译OK,为什么可以呢?因为unique_ptr提供了右值引用的拷贝构造和右值引用的赋值函数,如下
unique_ptr<int>(const unique_ptr<int> && src){};
unique_ptr<int> & operator=(const unique_ptr<int> && src)){};
return 0;
}
//推荐使用
2:带引用计数的智能指针(share_ptr,weak_ptr)
带引用计数的好处:多个智能指针可以管理同一个资源
带引用计数:给每一个对象资源,匹配一个引用计数,
智能指针引用一个资源的时候,给这个资源引用计数加1
当这个智能指针出作用域不再使用资源的时候,给这个资源引用计数-1,当引用计数不为0的时候,还不能析构这个资源,
当引用计数为0的时候,说明已经没有外部资源使用这个资源了,那么就可以析构这个资源了
代码3 简单实现share_ptr
#include <iostream>
using namespace std;
template<typename T>
class RefCount {
public:
RefCount(T * pSrc = nullptr, int refCount = 0):_pSrc(pSrc),_refCount(refCount) {
}
void addCount() { this->_refCount++; }
void deleltCount() { --this->_refCount; }
int refCount() { return this->_refCount; }
private:
T * _pSrc;
int _refCount = 0;
};
template<typename T>
class MySmartPtr2 {
public:
//新创建的智能指针,默认计数器为1
MySmartPtr2<T> (T * mptr=nullptr): _mptr(mptr){
_pRef = new RefCount<T>(_mptr,1);
}
//拷贝构造
MySmartPtr2<T>(const MySmartPtr2<T> & _rval) {
//两个智能指针指向相同的资源
this->_mptr = _rval._mptr;
this->_pRef = _rval._pRef;
this->_pRef->addCount();
}
//赋值重载
MySmartPtr2<T> & operator=(const MySmartPtr2<T> & _rval) {
if (this == &_rval) { retur *this; }
else {
this->_pRef->deleltCount();
int currentCount = this->_pRef->refCount();
if (currentCount == 0) {
delete this->_mptr;//销毁指向的资源
this->_mptr = nullptr;
delete _pRef;
_rPef = nullptr;
}
this->_pRef = _rval._pRef;
this->_mptr = _rval._mptr;
this->_pRef->addCount();
return *this;
}
}
~MySmartPtr2<T>() {
this->_pRef->deleltCount();
int currentCount = this->_pRef->refCount();
if (currentCount == 0) {
delete this->_mptr;//销毁指向的资源
this->_mptr = nullptr;
delete _pRef;
_pRef = nullptr;
}
}
int getRefCount() { return this->_pRef->refCount(); }
private:
T * _mptr;
RefCount<T> * _pRef;
};
int main() {
MySmartPtr2<int> ms1(new int(100)) ;
{
MySmartPtr2<int> ms2(ms1);
cout << "RefCount=" << ms1.getRefCount() << endl;
MySmartPtr2<int> ms3(ms1);
cout << "RefCount=" << ms1.getRefCount() << endl;
}
cout << "RefCount=" << ms1.getRefCount() << endl;
system("pause");
return 0;
}
share_ptr: 强智能指针,可以改变资源的引用计数
weak_ptr: 弱智能指针,不会改变资源的引用计数
强智能指针:循环引用(交叉引用)是什么问题?什么结果?怎么解决?
交叉引用代码
class A{
pubic:
A(){cout<<"A()"<<endl;}
~A(){cou<<"~A()"<<endl;}
share_ptr<B> _ptrb;
}
class B{
pubic:
B(){cout<<"B()"<<endl;}
~B(){cou<<"~B()"<<endl;}
share_ptr<A> _ptrb;
}
int main(){
share_ptr<A> pa(new A());
share_ptr<B> pb(new B());
pa->_ptrb=pb;
pb->_ptra=pa;
cout<<pa.use_count()<<endl;// 2
cout<<pb.use_count()<<endl;// 2
}
上面代码造成new出来的资源无法释放!!资源泄漏问题
解决:
定义对象的时候,用强智能指针,引用对象的地方用弱智能指针
class A{
pubic:
A(){cout<<"A()"<<endl;}
~A(){cou<<"~A()"<<endl;}
void testA(){
cout<<"A testA() Function"<<endl;
}
weak_ptr<B> _ptrb;
}
class B{
pubic:
B(){cout<<"B()"<<endl;}
~B(){cou<<"~B()"<<endl;}
void function(){
share_ptr<A> _tp=_ptrb.lock();//提升方法
if(_tp!=nullptr){
_tp->testA();
}
}
weak_ptr<A> _ptrb; //weak_ptr 弱智能指针,不会改变引用计数
}
int main(){
share_ptr<A> pa(new A());
share_ptr<B> pb(new B());
pa->_ptrb=pb;
pb->_ptra=pa;
pb.function();
cout<<pa.use_count()<<endl;// 2
cout<<pb.use_count()<<endl;// 2
}
share_ptr 和 weak_ptr 是线程安全的.
<一>智能指针基础的更多相关文章
- Boost智能指针-基础知识
简单介绍 内存管理一直是 C++ 一个比較繁琐的问题,而智能指针却能够非常好的解决问题,在初始化时就已经预定了删除.排解了后顾之忧.1998年修订的第一版C++标准仅仅提供了一种智能指针:std::a ...
- C++ 中的智能指针-基础
简介 在现代 C++ 编程中,标准库包含了智能指针(Smart pointers). 智能指针用来确保程序不会出现内存和资源的泄漏,并且是"异常安全"(exception-safe ...
- C++ 基础知识回顾(string基础、智能指针、迭代器、容器类)
[1] string基础 [1.1] string 的构造 #include <iostream> #include <string> int main() { using n ...
- [易学易懂系列|rustlang语言|零基础|快速入门|(21)|智能指针]
[易学易懂系列|rustlang语言|零基础|快速入门|(21)|智能指针] 实用知识 智能指针 我们今天来讲讲Rust中的智能指针. 什么是指针? 在Rust,指针(普通指针),就是保存内存地址的值 ...
- c++基础 使用智能指针
三个智能指针模板(auto_ptr.unique_ptr和shard_ptr)都定义了类似指针的对象(c++11已将auto_ptr摒弃),可以将new获得(直接或间接) 的地址赋给这种对象.当智能指 ...
- ndk学习之c++语言基础复习----C++线程与智能指针
线程 线程,有时被称为轻量进程,是程序执行的最小单元. C++11线程: 我们知道平常谈C++线程相关的东东基本都是基于之后要学习的posix相关的,其实在C++11有自己新式创建线程的方法,所以先来 ...
- C++基础--智能指针
智能指针其实也不是完全的指针,应该说是像指针一样的类对象,智能指针通常有指针的功能,当然同时也包含了一些额外的功能.目前比较常见的智能指针有auto_ptr.unique_ptr和shared_ptr ...
- 【UE4 C++ 基础知识】<15> 智能指针 TSharedPtr、UniquePtr、TWeakPtr、TSharedRef
基本概念 UE4 对 UObject 对象提供垃圾回收 UE4 对原生对象不提供垃圾回收,需要手动进行清理 方式 malloc / free new / delete new与malloc的区别在于, ...
- C++智能指针
引用计数技术及智能指针的简单实现 基础对象类 class Point { public: Point(int xVal = 0, int yVal = 0) : x(xVal), y(yVal) { ...
- C++ 引用计数技术及智能指针的简单实现
一直以来都对智能指针一知半解,看C++Primer中也讲的不够清晰明白(大概是我功力不够吧).最近花了点时间认真看了智能指针,特地来写这篇文章. 1.智能指针是什么 简单来说,智能指针是一个类,它对普 ...
随机推荐
- 第五章:Admin管理后台 - 3:Admin文档生成器
Django的admindocs应用可以从模型.视图.模板标签等地方获得文档内容. 一.概览 要激活admindocs,请按下面的步骤操作: 在INSTALLED_APPS内添加django.cont ...
- windows系统下使用bat脚本文件设置 JDK 系统环境变量
号开头的行不要写在bat文件中 # java_init.bat # 注意文件换行符是windows系统下的(CR LF),文件编码是ANSI # path变量追加这个可以拓展到tomcat,mysql ...
- Grafana配置Alert监控告警
1.添加告警途径 这里以slack为例 测试是否可用 在slack上收到告警通知了 安装插件 # grafana-cli plugins install grafana-image-renderer ...
- Python离线安装Flask
受限于内网,无法使用pip install Flask直接安装. 以Flask-0.12.2为例 安装Flask需要以下的依赖性,在安装Flask离线版时可以看到依赖性要求. 离线安装文件地址: ht ...
- 使用Gitlab的CI/CD功能自动化推送docker镜像到Nexus仓库出现的问题
在服务器中可以直接使用命令行登录,推送docker镜像等 但是在使用Gitlab的CI/CD功能中,gitlab-ci.yml文件执行过程中出现如下错误: 原因分析: 服务器上之前使用命令行登陆过Ne ...
- HDU2041 超级楼梯 (线性DP)
fn[i]表示走上第i级台阶的所有走法. 方程:fn[i]=fn[i-1]+fn[i-2]; 1 #include<cstdio> 2 #define MAXN 40 3 using na ...
- 实例分析Scheduled Thread Pool Executor与Timer的区别
摘要:JDK 1.5开始提供Scheduled Thread PoolExecutor类,Scheduled Thread Pool Executor类继承Thread Pool Executor类重 ...
- 第一个java程序进行总结
1.java程序编写-编译-运行的过程 编写:我们将编写的java代码保存在以".java"结尾的源文件中 编译:使用javac.exe命令编译我们的java源文件.格式:java ...
- 驱动开发:内核枚举ShadowSSDT基址
在笔者上一篇文章<驱动开发:Win10枚举完整SSDT地址表>实现了针对SSDT表的枚举功能,本章继续实现对SSSDT表的枚举,ShadowSSDT中文名影子系统服务描述表,SSSDT其主 ...
- 我要手撕mybatis源码
传统的JDBC编程中的一般操作: 1.注册数据库驱动类,指定数据库的URL地址.数据库用户名.密码等连接信息 2.通过DriverManager打开数据库连接 3.通过数据库连接创建Statement ...