手动实现 shared_ptr
面试写了一个基础的 scoped_ptr,被面试官要求写 shared_ptr,一时语塞。面试官不断提示我说在现有的基础上实现 shared_ptr 很简单,真的很简单,宛如在不断暗示我 1+1 就是把两个数加在一起。我知道简单,也知道引用计数原理,但没有写过代码啊,不知道具体是怎么实现引用计数的,当时只能放弃了。
今天研究了一下 shared_ptr,手写了简单实现,现在记录一下实现的重点。
引用计数
面试时,我想是不是用 static 实现的引用计数?其实不能这样做,静态变量是同属一个类所有对象,这样的话这个类的所有对象,不管指向的是不是相同的内存,它们共享的都是同一个计数器。
引用计数,就是在内存中开辟一块内存专门用于存储资源被引用的次数,自己保留一个指向计数器的指针,计数器初始化为 1。每次用已有的 shared_ptr 构造新的 shared_ptr 或者给已有的 shared_ptr 赋值时,通过指针将计数器加 1,所有指向同一个资源的 shared_ptr 也指向同一个计数器。
释放所有权
当 shared_ptr 析构或主动释放所有权时,应将引用计数减 1,然后判断引用计数是否归零,如果归零,那么就释放指针指向的内存,此后将计数器指针置为空。需要注意的是,如果将计数器指针置为空,为了防止指针主动释放所有权之后计数器指针为空,导致判断计数是否归零,需要在判断计数归零前先判断计数指针是否为空。
参考代码
#include <iostream>
#include <string>
template <typename T>
class SharedPtr
{
private:
// 裸指针.
T *_ptr;
// 指向引用计数器的指针.
int *_pcount;
public:
SharedPtr(T *ptr) : _ptr(ptr), _pcount(new int(1))
{
std::cout << "SharedPtr " << this << " constructed!\n\n";
}
// 复制构造函数.
SharedPtr(const SharedPtr<T> &s) : _ptr(s._ptr), _pcount(s._pcount)
{
std::cout << "Entering copy constructor\n";
std::cout << "\tSharedPtr " << this
<< " copied from SharePtr " << &s << std::endl;
// 引用计数加一.
std::cout << "\tReference count increased by one\n";
++(*(_pcount));
std::cout << "Leaving copy constructor\n\n";
}
~SharedPtr()
{
std::cout << "Entering destructor\n";
// 重置指针为空.
this->_ptr = nullptr;
std::cout << "\tReference count decreased by one\n";
// 引用计数减一.
if (_pcount != nullptr && --(*(this->_pcount)) == 0)
{
delete this->_ptr;
_ptr = nullptr;
delete _pcount;
_pcount = nullptr;
std::cout << "\tCount zero, memory released\n";
}
// 将引用计数指针置为空.
this->_pcount = nullptr;
std::cout << "SharedPtr " << this << " destructed\n";
}
public:
// 返回引用计数的个数.
int use_count() const
{
return *(this->_pcount);
}
int use_count()
{
return *(this->_pcount);
}
// 返回指针是否具有独占权, 即引用计数是否为 1.
bool unique() const
{
return *(this->_pcount) == 1 ? true : false;
}
bool unique()
{
return *(this->_pcount) == 1 ? true : false;
}
// 返回裸指针.
T *get() const
{
return this->_ptr;
}
// 释放当前指针的资源所有权, 计数减一.
void release()
{
std::cout << "\tRelease resource ownership\n";
// 重置指针为空.
this->_ptr = nullptr;
std::cout << "\tReference count decreased by one\n";
// 将当前资源引用计数减一.
if (_pcount != nullptr && --(*(this->_pcount)) == 0)
{
delete this->_ptr;
delete this->_pcount;
this->_pcount = nullptr;
std::cout << "\tCount zero, memory released\n";
}
// 将引用计数指针置为空.
this->_pcount = nullptr;
}
public:
SharedPtr<T> &operator=(const SharedPtr<T> &s)
{
std::cout << "Entering assignment operator\n";
if (this == &s)
{
return *this;
}
// 释放原来的旧资源.
release();
// 新资源计数加一.
std::cout << "\tReference count increased by one\n";
this->_ptr = s._ptr;
this->_pcount = s._pcount;
++(*(this->_pcount));
std::cout << "Leaving assignment operator\n\n";
return *this;
}
T &operator*()
{
return *(this->_ptr);
}
T *operator->()
{
return this->_ptr;
}
};
int main()
{
SharedPtr<std::string> p1(new std::string("Hello world!"));
SharedPtr<std::string> p2(p1);
SharedPtr<std::string> p3 = p2;
p3 = p2;
std::cout << p2.use_count() << std::endl;
p1.release();
std::cout << p2.use_count() << std::endl;
std::cout << *(p2.get()) << std::endl;
return 0;
}
输出如下
SharedPtr 0x7ffffffedba0 constructed!
Entering copy constructor
SharedPtr 0x7ffffffedbb0 copied from SharePtr 0x7ffffffedba0
Reference count increased by one
Leaving copy constructor
Entering copy constructor
SharedPtr 0x7ffffffedbc0 copied from SharePtr 0x7ffffffedbb0
Reference count increased by one
Leaving copy constructor
Entering assignment operator
Release resource ownership
Reference count decreased by one
Reference count increased by one
Leaving assignment operator
3
Release resource ownership
Reference count decreased by one
2
Hello world!
Entering destructor
Reference count decreased by one
SharedPtr 0x7ffffffedbc0 destructed
Entering destructor
Reference count decreased by one
Count zero, memory released
SharedPtr 0x7ffffffedbb0 destructed
Entering destructor
Reference count decreased by one
SharedPtr 0x7ffffffedba0 destructed
手动实现 shared_ptr的更多相关文章
- C++11 shared_ptr 智能指针 的使用,避免内存泄露
多线程程序经常会遇到在某个线程A创建了一个对象,这个对象需要在线程B使用, 在没有shared_ptr时,因为线程A,B结束时间不确定,即在A或B线程先释放这个对象都有可能造成另一个线程崩溃, 所以为 ...
- 关于智能指针boost::shared_ptr
boost库中的智能指针shared_ptr, 功能强大, 且开销小,故受到广大coder的欢迎. 但在实际的使用过程中,笔者也发现了一些不足. 1.定制的删除器 shared_ptr除了可以使用默认 ...
- CentOS6.5升级手动安装gcc4.8.2
一.简易安装 操作环境 CentOS6.5 64bit,原版本4.4.7,不能支持C++11的特性~,希望升级到4.8.2 不能通过yum的方法升级,需要自己手动下载安装包并编译 1.1 获取安装包并 ...
- 【STL学习】智能指针之shared_ptr
前面已经学习过auto_ptr,这里补充另外一种智能指针,比auto_ptr要更强力更通用的shared_ptr. shared_ptr 简介及使用选择 几乎所有的程序都需要某种形式的引用计数智能指 ...
- [转] weak_ptr解决shared_ptr环状引用所引起的内存泄漏
http://blog.csdn.net/liuzhi1218/article/details/6993135 循环引用: 引用计数是一种便利的内存管理机制,但它有一个很大的缺点,那就是不能管理循环引 ...
- 解决eclipse无法解析shared_ptr
今天心血来潮更新了一下机器上的ubuntu,装了14.04版本,原来是32位的,换成64的之后感觉是快了不少(加了内存).因为不少软件没做备份,包括eclipse,所以只得重装,重装之后的麻烦事儿就是 ...
- c++继承构造子类调用父类构造函数的问题及关于容器指针的问题及当容器里储存指针时,记得要手动释放
看下面的一个问题: class Person { private: string name; public: Person(const string& s=""){ nam ...
- stl中auto_ptr,unique_ptr,shared_ptr,weak_ptr四种智能指针使用总结
stl中auto_ptr,unique_ptr,shared_ptr,weak_ptr四种智能指针使用总结 1. auto_ptrauto_ptr主要是用来解决资源自动释放的问题,比如如下代码:voi ...
- weak_ptr解决shared_ptr环状引用所引起的内存泄漏[转]
转载:http://blog.csdn.net/liuzhi1218/article/details/6993135 循环引用: 引用计数是一种便利的内存管理机制,但它有一个很大的缺点,那就是不能管理 ...
随机推荐
- C#多线程详解(一) Thread.Join()的详解
bicabo C#多线程详解(一) Thread.Join()的详解 什么是进程?当一个程序开始运行时,它就是一个进程,进程包括运行中的程序和程序所使用到的内存和系统资源.而一个进程又是由多个线程 ...
- SpringBoot博客开发之AOP日志处理
日志处理: 需求分析 日志处理需要记录的是: 请求的URL 访问者IP 调用的方法 传入的参数 返回的内容 上面的内容要求在控制台和日志中输出. 在学习这部分知识的时候,真的感觉收获很多,在之前Spr ...
- Promise.race()
Promise.race([ ])---race竞赛,只要有一个决议了,就返回一个promise实例(对应resolve()或reject( )中参数值: 1.与Promise.all()对应的,还有 ...
- 聚类算法与K-means实现
聚类算法与K-means实现 一.聚类算法的数学描述: 区别于监督学习的算法(回归,分类,预测等),无监督学习就是指训练样本的 label 未知,只能通过对无标记的训练样本的学习来揭示数据的内在规律和 ...
- scrapy抓取的页面中文会变成unicode字符串
不了解编码的,需要先补下:http://www.cnblogs.com/jiangtu/p/6245264.html 在学习&使用scrapy抓取网上信息时,发现scrapy 会将含有中文的f ...
- Gitlab(1)- 简单介绍
什么是 Gitlab 一个开源分布式版本控制系统 开发语言:Ruby 功能:管理项目源代码.版本控制.代码复用与查找.权限管控 Git 家族成员 Git:是一种版本控制系统,是一个命令,是一种工具 G ...
- Spring AOP框架 AspectJ
1 AspectJ简介 v AspectJ是一个基于Java语言的AOP框架 v Spring2.0以后新增了对AspectJ切点表达式支持 v @AspectJ 是AspectJ1.5新增功能 ...
- Golang入门学习(三):函数
文章目录 2.3 函数 2.3.1 基本语法 2.3.2 入门demo: 2.3.3 函数递归: 2.3.4 函数注意事项 2.3.5 init函数 2.3.6 匿名函数 2.3.7 闭包 2.3.8 ...
- 在C#中使用RSA进行加密和解密
这篇文章向您展示了如何在c#.net Windows窗体应用程序中使用RSA算法对字符串进行加密和解密.RSA是由Ron Rivest,Adi Shamir和Leonard Adleman开发的非对称 ...
- 判断input radio选中那个
var _sex=$("input[name='sex']:checked").val(); if(_sex==null){ layer.msg("请选择性别" ...