C++ 智能指针(unique_ptr / shared_ptr)代码实现
文章目录
- unique_ptr 智能指针的实现
- shared_ptr 智能指针的实现
- 指针类型转换
unique_ptr 智能指针的实现
一个对象只能被单个unique_ptr 所拥有。
#include <iostream>using namespace std;/*增加模板类型,保证智能指针的类型是由传入的类型决定的*/
template <typename T>class smart_ptr {
public:explicit smart_ptr(T *ptr = nullptr): ptr_(ptr){}/*移动构造函数,当前智能指针仅能被一个对象引用*/smart_ptr(smart_ptr&& other) {ptr_ = other.release();}/*如果我们想要消除移动构造和拷贝构造,只需要将其 构造标记为delete*///smart_ptr(const smart_ptr&) = delete;//smart_ptr& operator=(const smart_ptr&) = delete;/*在构造参数时直接生成新的智能指针,从而不再需要在函数体中构造临时对象,当前支持移动构造进行函数对象的构造。*/smart_ptr& operator=(smart_ptr rhs) {rhs.swap(*this);return *this;} T* release() {T* ptr = ptr_;ptr_ = nullptr;return ptr;}void swap(smart_ptr& rhs) {using std::swap;//标准模板库的交换函数swap(rhs.ptr_,ptr_);}~smart_ptr(){delete ptr_;}T* get() const{ return ptr_;}/*指针的特性,可以通过*解引用访问 */T& operator*() const{return *ptr_;}/*指针的特性,可以通过-> 访问指针的地址内容*/T* operator->() const{return ptr_;}/*重载bool运算符,能够让smart_ptr像指针一样用在布尔表达式中*/operator bool() const{return ptr_;}private:T* ptr_;
};class Shape{public:virtual void create_shape() = 0;virtual ~Shape(){}
};class Circle: public Shape{public:Circle(){cout << "Circle::Circle()" << endl;}void create_shape(){cout << "create shape: Circle" << endl;}~Circle(){cout << "Circle::delete()" << endl;}
};class Triangle: public Shape{public:Triangle(){cout << "Triangle::Tirangle()" << endl;}void create_shape(){cout << "create shape: Triangle" << endl;}~Triangle(){cout << "Triangle::delete()" << endl;}
};int main()
{smart_ptr<Shape> ptr1(new Circle);smart_ptr<Shape> ptr2(nullptr);smart_ptr<Shape> ptr3 ;//ptr3 = ptr1; //编译报错,赋值需要一个对象(而非引用),因而进入执行之前需要引发一个构造,但是此时没有可用的构造函数ptr3 = std::move(ptr2); // 编译正常,支持移动构造 return 0;
}
shared_ptr 智能指针的实现
多个shared_ptr可以共享同一个对象,当他们全部失效的时候,这个对象才会被删除。
此时对shared_ptr的需求是共享 同一个对象时也需要共享同一个计数,当最后一个指向对象(和共享计数)的shared_ptr析构时,它需要删除对象和共享计数。
在以上unique_ptr的实现基础上增加引用计数相关的操作,实现如下:
#include <iostream>using namespace std;/*增加一个引用计数类,来记录当前对象被智能指针引用的次数*/
class shared_count{
public:shared_count():count_(1) {}void add_count(){++count_;}long reduce_count(){return --count_;}long get_count() {return count_;}
private:long count_;
};/*增加类模板,保证智能指针的类型是由传入的类型决定的*/
template <typename T>class smart_ptr {
public:explicit smart_ptr(T *ptr = nullptr): ptr_(ptr){if(ptr) {shared_count_ = new shared_count();}}/*移动构造函数,当前智能指针仅能被一个对象引用,同时不进行引用计数的自加*/template <typename U>smart_ptr(smart_ptr<U>&& other) {ptr_ = other.release();if(ptr_) {shared_count_ = other.shared_count_;other.ptr_ = nullptr;}}/*拷贝构造函数,支持子类对象向父类对象的拷贝*/template <typename U>smart_ptr(const smart_ptr<U>& other) {ptr_ = other.ptr_;if(ptr_) {other.shared_count_ -> add_count();shared_count_ = other.shared_count_;}}/*同类型对象的拷贝构造函数*/smart_ptr(smart_ptr & other) {ptr_ = other.ptr_;if(ptr_) {other.shared_count_ -> add_count();shared_count_ = other.shared_count_;}}/*在构造参数时直接生成新的智能指针,从而不再需要在函数体中构造临时对象,当前支持移动构造进行函数对象的构造。*/smart_ptr& operator=(smart_ptr rhs) {rhs.swap(*this);return *this;} T* release() {T* ptr = ptr_;ptr_ = nullptr;return ptr;}void swap(smart_ptr& rhs) {using std::swap;swap(rhs.ptr_,ptr_);swap(rhs.shared_count_,shared_count_);}long use_count() const {if(ptr_) {return shared_count_->get_count();} else {return 0;}}~smart_ptr(){// cout << "smart_ptr::delete count is " << shared_count_ -> get_count() << endl;if(ptr_ && !shared_count_ -> reduce_count()){delete ptr_;delete shared_count_;}}T* get() const{ return ptr_;}T& operator*() const{return *ptr_;}T* operator->() const{return ptr_;}operator bool() const{return ptr_;}private:T* ptr_;shared_count* shared_count_;//统计对象引用计数的类
};class Shape{public:virtual void create_shape() = 0;virtual ~Shape(){}
};class Circle: public Shape{public:Circle(){cout << "Circle::Circle()" << endl;}void create_shape(){cout << "create shape: Circle" << endl;}~Circle(){cout << "Circle::delete()" << endl;}
};class Triangle: public Shape{public:Triangle(){cout << "Triangle::Tirangle()" << endl;}void create_shape(){cout << "create shape: Triangle" << endl;}~Triangle(){cout << "Triangle::delete()" << endl;}
};int main()
{smart_ptr<Shape> ptr1(new Circle);cout << "use count of ptr1 is: " << ptr1.use_count() << endl;smart_ptr<Shape> ptr2;cout << "use count of ptr2 was: " << ptr2.use_count() << endl;//ptr2 = std::move(ptr1); // 移动拷贝构造,不进行计数增加(可查看以上的实现)ptr2 = ptr1; //普通的拷贝构造,支持多个指针共享同一个对象,则对应智能指针的共享计数增加cout << "use count of ptr2 is now: " << ptr2.use_count() << endl;if(ptr1) {cout << "ptr1 is not empty " << endl; }return 0;
}
输出如下:
Circle::Circle()
use count of ptr1 is: 1
use count of ptr2 was: 0
use count of ptr2 is now: 2
Circle::delete()
指针类型转换
C++已有的强制类型转换有如下几种:
- static_cast 用于类层次结构中基类和派生类之间指针或引用的转换
- reinterpret_cast 改变指针或引用的类型、将指针或引用转换为一个足够长度的整形、将整型转换为指针或引用类型
- const_cast 用于强制去掉不能被修改的常数特性
- dynamic_cast dynamic_cast是运行时处理的,运行时要进行类型检查。
智能指针需要实现类似的函数模板,想要达到以上对应的强制类型转换的功能,我们需要增加构造函数,且允许在对智能指针内部的指针对象赋值时,使用一个现有的智能指针的共享计数。如下所示:
template <typename U>
smart_ptr(const smart_ptr<U> &other, T* ptr) { //拷贝构造时,使用T* ptr进行类型转换_ptr = ptr;if(_ptr) {other.shared_count_ -> add_count();shared_count_ = other.shared_count_;}
}
根据以上代码,实现dynamic_pointer_cast
template <typename T, typename U>
smart_ptr <T> dynamic_pointer_cast(const smart_ptr<U> &other) {T *ptr = dynamic_cast<T*> (other.get());return smart_ptr<T> (other,ptr);
}
使用方式如下:
smart_ptr<Circle> ptr3 = dynamic_pointer_cast<Circle> ptr2;
同理实现其他三个 强制类型转换的逻辑实现如下:
template <typename T, typename U>
smart_ptr <T> static_pointer_cast(const smart_ptr<U> &other) {T *ptr = static_cast<T*> (other.get());return smart_ptr<T> (other,ptr);
}template <typename T, typename U>
smart_ptr <T> reinterpret_pointer_cast(const smart_ptr<U> &other) {T *ptr = reinterpret_cast<T*> (other.get());return smart_ptr<T> (other,ptr);
}template <typename T, typename U>
smart_ptr <T> const_pointer_cast(const smart_ptr<U> &other) {T *ptr = const_cast<T*> (other.get());return smart_ptr<T> (other,ptr);
}
相关文章:

winform实现截图
这个截图模仿QQ截图,左键单击开始截图,鼠标移动出现方框确定截图尺寸,放开时为最终尺寸,双击鼠标弹出保存对话框进行保存。 还有一点就是,如果截图尺寸方框已经确定,移动鼠标到所选区域内时,鼠标…

java interface list_你了解注解内的@interface List么
Annotation, Multi-valued annotation, nested annotation, 多值注解, 嵌套注解今天在研究Spring MVC的Validation机制时(这里 | 还有这里),简单看了下一些注解的源码,比如Min,发现以前从来没注意到的注解写法。看来基础知识有疏漏啊.../*** …

Struts 2的输入校验(一)
9 Struts 2的输入校验输入校验有两种:客户端和服务器端校验。客户端校验一般是通过JavaScript来完成,这种校验可减轻服务器压力。服务器校验主要通过服务器端编程的方式来完成。(1) 客户端校验客户端校验一般是通过JavaScript来完成,这种校验…

通过document.domain实现跨域访问
通过document.domain实现跨域访问:https://blog.csdn.net/nlznlz/article/details/79506655 前端跨域方法之document.domain和location.hash:https://blog.csdn.net/WEB_YH/article/details/79364565 转载于:https://www.cnblogs.com/bydzhangxiaowei/p/…

设计模式 之美 -- 原型模式
文章目录1. 解决问题2. 应用场景3. 实现方式C实现C语言实现4. 缺点5. 和其他三种创建模式的对比(单例,工厂,建造者)1. 解决问题 如果对象的创建成本较大,而同一个类的不同对象之间的差别不大(大部分字段相…

Objective-C语法简记
开始学习iPhone开发了,虽然现在已经有了Swift,但我还是老老实实地学习Objective-C,鄙人入门的程序语言是C,后来学习了C#和Java,现在来学Objective-C,这篇只是一些很简略的笔记,不算是语法书。 代…

java编写最大公约数_Java编写最大公约数和最小公倍数
package javaapplication24;class NegativeIntegerException extends Exception{String message;public NegativeIntegerException(){message"方法的参数值不是正整数";}public String toString(){return message;}}class MaxCommonDivisor{public int getMaxCommonD…
肤色检测算法 - 基于不同颜色空间简单区域划分的皮肤检测算法
由于能力有限,算法层面的东西自己去创新的很少,很多都是从现有的论文中学习,然后实践的。 本文涉及的很多算法,在网络上也有不少同类型的文章,但是肯定的一点就是,很多都是不配代码的,或者所附带…

【算法学习】堆排序建立最大堆
本文代码均转自: 作者:早就戒了 来源:CSDN 原文:https://blog.csdn.net/qq_37169817/article/details/79777264 版权声明:本文为博主原创文章,转载请附上博文链接! 1 public class HeapSort { 2…

设计模式 之美 -- 代理模式
文章目录1. 解决问题2. 应用场景1. 业务系统的非功能性开发2. 代理模式在RPC、缓存中的应用3. 实现C实现C语言实现1. 解决问题 客户端和目标对象之间需要进行交互,此时客户端类和目标对象类相关操作之间的逻辑如果交合在一起,会导致客户端和目标对象模块…

java中注解的使用_java中注解的使用
使用过ssh框架的人一定也使用过注解,尤其是在spring框架中,注解可谓是spring容器和AOP编程的重要环节。注解就是用于修饰类、全局变量、方法、参数或局部变量的接口,java中规定,注解的使用不允许影响其修饰类的存在,也…

水题/poj 1852 Ants
1 /*2 PROBLEM:poj18523 AUTHER:Nicole4 MEMO:水题5 */6 #include<cstdio>7 using namespace std;8 int cmax(int a,int b){return a>b?a:b;}9 int cmin(int a,int b){return a<b?a:b;} 10 int main() 11 { 12 int cases; 13 scanf("%d…

素数、最大公约数、最下公倍数、质因数分解
2013-08-18 11:20:43 素数、最大公约数、最下公倍数、质因数分解都是与素数相关的,解决了素数的问题,其他的都可以此为基础求解。 小结: 求1到n之间的素数的基本方法是通过遍历2到sqrt(n),判断每个数是否是素数来得到,…

Spring注解 开发
转载于:https://www.cnblogs.com/JBLi/p/10489541.html

读书:个人成长 -- 即兴演讲
与人交流时,有人发言语无伦次,舌头像打了结。 有人一开讲便滔滔不绝,却毫无重点。 也有人说话索然无味,没法让你投以关注。 如何在任何场合游刃有余地表达? 如何掌控此时此刻,用说话影响他人? …

php mysql环境搭配_centos6.7下搭配apache php mysql环境
安装过程安装apacheapache默认端口为80, 而nginx默认端口也是80, 所以安装apache前, 检查是否安装了nginx, 确保80端口没有被占用, 然后执行以下命令安装apacheyum install httpd httpd-devel启动apache服务/etc/init.d/httpd start或service httpd start停止apache服务/etc/in…

我们如此努力,也不过是个普通人
http://www.nowamagic.net/librarys/eight/posts/2500文 / 張君雅 南方日报曾刊登了一条新闻,大意是说有个女孩子以她的成绩考入北大清华没问题。但她从小参加各种社会活动,深受曾留学法国的母亲“生命的意义在于体验最多而不是最好”影响,决…

Citrix XenServer@cloudstack基本功能测试报告2
Cloudstack功能测试1、创建Zone、Pod、Clusters,添加hosts、Primary Storage、Secondary Storage步骤:1、 登录cloudstack控制板2、 选择基础架构,选择区域,点击查看全部,然后点击添加区域3、 选择基本区域类型4、 输入…

ABP中的Filter(下)
接着上面的一个部分来叙述,这一篇我们来重点看ABP中的AbpUowActionFilter、AbpExceptionFilter、AbpResultFilter这三个部分也是按照之前的思路来一个个介绍,当然这里面如果和前面的Interceptor有重复的部分,那么将会对两者进行一个对比并作出…

LRU算法 -- 链表 完整实现
LRU算法(Least Recently Used) 算是我们经常遇到的一种淘汰算法,其中内存管理模块进行内存页回收时有用到,针对不经常使用的内存页,LRU淘汰策略能够将该内存页回收给操作系统。 属于 我们操作系统设计中的 时间局部性原理,最长时…

python getostime_python中sys,os,time模块的使用(包括时间格式的各种转换)
sys模块sys.argv: 实现从程序外部向程序传递参数。位置参数argv[0]代表py文件本身,运行方法 python xx.py 参数1,参数2 。。self sys.argv[0]name sys.argv[1]age sys.argv[2]print self, name, agesys.getdefaultencoding(): 获取系统当前编码&#…

关于SpringMVC和Struts2的区别
1. 与struts2不同 1、 springmvc的入口是一个servlet即前端控制器,而struts2入口是一个filter过虑器。 2、 springmvc是基于方法开发,传递参数是通过方法形参,可以设计为单例或多例(建议单例),struts2是基于类开发,…

DC-RC加固修补型砂浆
DC-RC加固修补型砂浆www.hrbjg.net一、DC-RC加固修补型砂浆的产品特点:1、耐火、耐高温、耐腐蚀、耐老化性能优良。2、强度高,抹灰操作性好。3、与原混凝土结构的粘结性能良好。4、无收缩,基本不会产生裂缝。5、二氧化碳、氯化物等透过性差&a…

类,实例,属性习题
class Restaurant(): def __init__(self,restaurant_name,cuisine_type): self.restaurant_namerestaurant_name self.cuisine_typecuisine_type def describle_restaurant(self): print("打印的第一条消息") print("打印的第…

数据结构和算法 -- 学习导图
数据结构和算法 是作为程序员写出高效代码的基础,为了今后的两年在高效代码之路上持续精进,将按照此学习导图进行 算法和数据结构的刻意练习,同时也希望为同样有高效代码追求的伙伴们提供一条学习路径,共同进步。 以下为今后持续…

HDU 1248 寒冰王座(全然背包:入门题)
HDU 1248 寒冰王座(全然背包:入门题) http://acm.hdu.edu.cn/showproblem.php?pid1248 题意: 不死族的巫妖王发工资拉,死亡骑士拿到一张N元的钞票(记住,仅仅有一张钞票),为了防止自己在战斗中频繁的死掉,他决定给自己买一些道具,于是他来到了地精商店前. 死亡骑士:"我要买…

java 彩票系统_JAVA版彩票随机生成系统
import java.io.*;import java.util.Random;class num{public static void main(String[]args){//声明一个随机数组int sjsh[]new int[7];int sum;try{InputStreamReader anew InputStreamReader(System.in);BufferedReader bnew BufferedReader(a);System.out.println ("…

Windows Server 2012 文件服务器群集
概述:之前已经测试了Windows Server 2012系统群集、Hyper-V群集,接下来将测试Windows Server 2012 文件服务器群集功能。实验环境:4台服务器都为Windows Server 2012 DataCenter操作系统在之前配置了群集的基础上,SRV2012服务器新…

023 判断出栈顺序是否正确
1.题目 输入两个整数序列,第一个序列表示栈的压入顺序,请判断第二个序列是否为该栈的弹出顺序。 假设压入栈的所有数字均不相等。例如序列1、2、3、4、5是某栈的压栈序列,序列4、5、3、2、1是该压栈序列对应的一个弹出序列,但4、3…

栈 -- 顺序栈、链式栈的实现 及其应用(函数栈,表达式求值,括号匹配)
文章目录实现顺序栈实现链式栈实现应用函数栈 的应用表达式求值中 的应用括号匹配中 的应用我们使用浏览器的时候经常会用到前进、后退功能。依次访问完一串页面 a – b – c之后点击后退功能,则能够依次看到c – b – a的页面。但是这个过程中,如果后退…