手动实现 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 循环引用: 引用计数是一种便利的内存管理机制,但它有一个很大的缺点,那就是不能管理 ...
随机推荐
- java多线程的一些面试题
8.callable与fature Callable与Runnable类似,但是Callable有返回值,并且有一个参数化的类型. Fature保存异步计算的结果.9.执行器 Executor.10. ...
- 云原生数据库 TDSQL-C 产品概述、产品优势、应用场景
云原生数据库 TDSQL-C(Cloud Native Database TDSQL-C,TDSQL-C)是腾讯云自研的新一代高性能高可用的企业级分布式云数据库.融合了传统数据库.云计算与新硬件技术的 ...
- 用CUDA写出比Numpy更快的规约求和函数
技术背景 在前面的几篇博客中我们介绍了在Python中使用Numba来写CUDA程序的一些基本操作和方法,并且展示了GPU加速的实际效果.在可并行化的算法中,比如计算两个矢量的加和,或者是在分子动力学 ...
- Python3-sqlalchemy-orm 分组统计
#-*-coding:utf-8-*- #__author__ = "logan.xu" import sqlalchemy from sqlalchemy import crea ...
- JS 之 每日一题 之 算法 ( 有多少小于当前数字的数字 )
给你一个数组 nums,对于其中每个元素 nums[i],请你统计数组中比它小的所有数字的数目. 换而言之,对于每个 nums[i] 你必须计算出有效的 j 的数量,其中 j 满足 j != i 且 ...
- CGLib 简析
背景 JDK 动态代理存在的一些问题: 调用效率低 JDK 通过反射实现动态代理调用,这意味着低下的调用效率: 每次调用 Method.invoke() 都会检查方法的可见性.校验参数是否匹配,过程涉 ...
- throw关键字
1.基础用法 2.方法中加合法校验,告知方法的调用者 数组越界判断 3.一切皆为对象,创建的是运行期对象,则可以不处理(throws/try catch),直接交给JVM处理(打印并终止程序) 4.O ...
- nacos配置中心模块详解
本文已收录 https://github.com/lkxiaolou/lkxiaolou 欢迎star. 配置中心 业务上的配置,功能开关,服务治理上对弱依赖的降级,甚至数据库的密码等,都可能用到动态 ...
- iNeuOS工业互联平台,PLC监测与控制应用过程案例。新闻:.NET 6 RC1 正式发布
目 录 1. 概述... 1 2. 平台演示... 2 3. 应用过程... 2 1. 概述 iNeuOS工业互联网操作系统主要使用.netcore 3. ...
- angularjs $http.get 和 $http.post 传递参数
$http.get请求数据的格式 $http.get(URL,{ params: { "id":id } }) .success(function(response, status ...