面试写了一个基础的 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的更多相关文章

  1. C++11 shared_ptr 智能指针 的使用,避免内存泄露

    多线程程序经常会遇到在某个线程A创建了一个对象,这个对象需要在线程B使用, 在没有shared_ptr时,因为线程A,B结束时间不确定,即在A或B线程先释放这个对象都有可能造成另一个线程崩溃, 所以为 ...

  2. 关于智能指针boost::shared_ptr

    boost库中的智能指针shared_ptr, 功能强大, 且开销小,故受到广大coder的欢迎. 但在实际的使用过程中,笔者也发现了一些不足. 1.定制的删除器 shared_ptr除了可以使用默认 ...

  3. CentOS6.5升级手动安装gcc4.8.2

    一.简易安装 操作环境 CentOS6.5 64bit,原版本4.4.7,不能支持C++11的特性~,希望升级到4.8.2 不能通过yum的方法升级,需要自己手动下载安装包并编译 1.1 获取安装包并 ...

  4. 【STL学习】智能指针之shared_ptr

    前面已经学习过auto_ptr,这里补充另外一种智能指针,比auto_ptr要更强力更通用的shared_ptr. shared_ptr 简介及使用选择  几乎所有的程序都需要某种形式的引用计数智能指 ...

  5. [转] weak_ptr解决shared_ptr环状引用所引起的内存泄漏

    http://blog.csdn.net/liuzhi1218/article/details/6993135 循环引用: 引用计数是一种便利的内存管理机制,但它有一个很大的缺点,那就是不能管理循环引 ...

  6. 解决eclipse无法解析shared_ptr

    今天心血来潮更新了一下机器上的ubuntu,装了14.04版本,原来是32位的,换成64的之后感觉是快了不少(加了内存).因为不少软件没做备份,包括eclipse,所以只得重装,重装之后的麻烦事儿就是 ...

  7. c++继承构造子类调用父类构造函数的问题及关于容器指针的问题及当容器里储存指针时,记得要手动释放

    看下面的一个问题: class Person { private: string name; public: Person(const string& s=""){ nam ...

  8. stl中auto_ptr,unique_ptr,shared_ptr,weak_ptr四种智能指针使用总结

    stl中auto_ptr,unique_ptr,shared_ptr,weak_ptr四种智能指针使用总结 1. auto_ptrauto_ptr主要是用来解决资源自动释放的问题,比如如下代码:voi ...

  9. weak_ptr解决shared_ptr环状引用所引起的内存泄漏[转]

    转载:http://blog.csdn.net/liuzhi1218/article/details/6993135 循环引用: 引用计数是一种便利的内存管理机制,但它有一个很大的缺点,那就是不能管理 ...

随机推荐

  1. Fancytree实例

    一.实例 <!DOCTYPE html> <html xmlns="http://www.w3.org/1999/xhtml"> <head> ...

  2. mfc HackerTools远程线程注入

    在一个进程中,调用CreateThread或CreateRemoteThreadEx函数,在另一个进程内创建一个线程(因为不在同一个进程中,所以叫做远程线程).创建的线程一般为Windows API函 ...

  3. Semaphore 的使用思路

    转自:https://www.cnblogs.com/klbc/p/9500947.html 最近在看一本书<Java并发编程 核心方法与框架>,打算一边学习一边把学习的经验记下来,所粘贴 ...

  4. springboot&&springcloud知识点

    spring cloud 常见面试题: A.https://blog.csdn.net/panhaigang123/article/details/79587612 B.https://blog.cs ...

  5. springboot开启矩阵传参MatrixVariable

    /** *   根据 URI 规范 RFC 3986 中 URL 的定义,路径片段中可以可以包含键值对. * 规范中没对对应的术语.一般 "URL 路径参数" 可以被应用,尽管更加 ...

  6. maven下载出错

    求解

  7. 查看局域网内所有的主机名、MAC地址和IP地址

    查看所有 IP at MAC $ arp -a ? (10.125.49.187) at 18:81:e:eb:ef:c0 on en0 ifscope [ethernet] ? (10.125.50 ...

  8. 【CSS】计数器

    抄自B站Up主CodingStartup起码课 <!DOCTYPE html> <html lang="en"> <head> <meta ...

  9. 20210714 noip15

    考前 mtr 中午拿着笔记本改题(Orz),一点多发现 13.50 有比赛(截止 12 点都没放出来),赶紧睡.13.40 到了学校,巨瞌睡,洗了把脸到机房发现推迟到 14.30 了,wcnm 趴在桌 ...

  10. Selenium系列(十九) - Web UI 自动化基础实战(6)

    如果你还想从头学起Selenium,可以看看这个系列的文章哦! https://www.cnblogs.com/poloyy/category/1680176.html 其次,如果你不懂前端基础知识, ...