Make_shared - 自己的实现

2024-01-23

我正在尝试自己实现shared_ptr。我有以下问题make_shared。主要特点是std::make_shared它在连续的内存块中分配计数器块和对象。我怎样才能做同样的事情?

我尝试做类似的事情:

template<class T>
class shared_ptr
{
private:
    class _ref_cntr
    {
    private:
        long counter;

    public:
        _ref_cntr() :
            counter(1)
        {
        }

        void inc()
        {
            ++counter;
        }

        void dec()
        {
            if (counter == 0)
            {
                throw std::logic_error("already zero");
            }

            --counter;
        }

        long use_count() const
        {
            return counter;
        }
    };

    template<class _T>
    struct _object_and_block
    {
        _T object;
        _ref_cntr cntr_block;

        template<class ... Args>
        _object_and_block(Args && ...args) :
            object(args...)
        {
        }
    };

    T* _obj_ptr;
    _ref_cntr* _ref_counter;

    void _check_delete_ptr()
    {
        if (_obj_ptr == nullptr)
        {
            return;
        }

        _ref_counter->dec();

        if (_ref_counter->use_count() == 0)
        {
            _delete_ptr();
        }

        _obj_ptr = nullptr;
        _ref_counter = nullptr;
    }

    void _delete_ptr()
    {
        delete _ref_counter;
        delete _obj_ptr;
    }

    template<class _T, class ... Args>
    friend shared_ptr<_T> make_shared(Args && ... args);

public:
    shared_ptr() :
        _obj_ptr(nullptr),
        _ref_counter(nullptr)
    {
    }

    template<class _T>
    explicit shared_ptr(_T* ptr)
    {
        _ref_counter = new counter_block();
        _obj_ptr = ptr;
    }

    template<class _T>
    shared_ptr(const shared_ptr<_T> & other)
    {
        *this = other;
    }

    template<class _T>
    shared_ptr<T> & operator=(const shared_ptr<_T> & other)
    {
        _obj_ptr = other._obj_ptr;
        _ref_counter = other._ref_counter;

        _ref_counter->inc();

        return *this;
    }

    ~shared_ptr()
    {
        _check_delete_ptr();
    }

};

template<class T, class ... Args>
shared_ptr<T> make_shared(Args && ... args)
{
    shared_ptr<T> ptr;
    auto tmp_object = new shared_ptr<T>::_object_and_block<T>(args...);
    ptr._obj_ptr = &tmp_object->object;
    ptr._ref_counter = &tmp_object->cntr_block;

    return ptr;
}

但是当我删除对象和计数器块时,会发生无效堆块异常。


N.B. _T is a 保留名称 https://stackoverflow.com/q/228783/981959并且您不得将其用于您自己的类型/变量/参数等的名称。

问题就在这里:

void _delete_ptr()
{
    delete _ref_counter;
    delete _obj_ptr;
}

这是错误的make_shared情况是因为您没有分配两个单独的对象。

采取的方法是make_shared在 Boost 和 GCC 中shared_ptr就是使用新的派生类型控制块,它在基类中包含引用计数,并为派生类型中的托管对象添加存储空间。如果你做_ref_cntr负责通过虚拟函数删除对象,然后派生类型可以覆盖该虚拟函数以执行不同的操作(例如,仅使用显式析构函数调用来销毁对象而不释放存储)。

如果你给_ref_cntr然后是一个虚拟析构函数delete _ref_counter将正确地破坏派生类型,因此它应该变成这样:

void _delete_ptr()
{
    _ref_counter->dispose();
    delete _ref_counter;
}

虽然如果您不打算添加weak_ptr支持,那么就不需要将托管对象和控制块的销毁分开,您可以让控制块的析构函数同时执行这两项操作:

void _delete_ptr()
{
    delete _ref_counter;
}

您当前的设计无法支持一个重要属性shared_ptr,这就是template<class Y> explicit shared_ptr(Y* ptr)构造函数必须记住原始类型ptr并在其上调用删除,而不是在_obj_ptr(已转换为T*)。请参阅note http://www.boost.org/libs/smart_ptr/shared_ptr.htm#pointer_constructor在相应构造函数的文档中boost::shared_ptr。为了使这项工作发挥作用_ref_cntr需要使用类型擦除来存储原始指针,与_obj_ptr in the shared_ptr对象,使得_ref_cntr::dispose()可以删除正确的值。设计上的改变也需要支持别名构造函数 http://www.boost.org/doc/libs/1_57_0/libs/smart_ptr/shared_ptr.htm#aliasing_constructor.

class _ref_cntr
{
private:
    long counter;

public:
    _ref_cntr() :
        counter(1)
    {
    }

    virtual ~_ref_cntr() { dispose(); }

    void inc()
    {
        ++counter;
    }

    void dec()
    {
        if (counter == 0)
        {
            throw std::logic_error("already zero");
        }

        --counter;
    }

    long use_count() const
    {
        return counter;
    }

    virtual void dispose() = 0;
};

template<class Y>
struct _ptr_and_block : _ref_cntr
{
    Y* _ptr;
    explicit _ptr_and_block(Y* p) : _ptr(p) { }
    virtual void dispose() { delete _ptr; }
};

template<class Y>
struct _object_and_block : _ref_cntr
{
    Y object;

    template<class ... Args>
    _object_and_block(Args && ...args) :
        object(args...)
    {
    }

    virtual void dispose() { /* no-op */ }
};

通过这样的设计,make_shared变成:

template<class T, class ... Args>
shared_ptr<T> make_shared(Args && ... args)
{
    shared_ptr<T> ptr;
    auto tmp_object = new shared_ptr<T>::_object_and_block<T>(args...);
    ptr._obj_ptr = &tmp_object->object;
    ptr._ref_counter = tmp_object;

    return ptr;
}

So _ref_counter指向分配的控制块,当你这样做时delete _ref_counter这意味着你有一个正确匹配的new/delete分配和释放同一对象的对,而不是使用创建一个对象new然后尝试delete两个不同的对象。

To add weak_ptr支持您需要向控制块添加第二个计数,并将调用移至dispose()在析构函数之外,因此当第一个计数变为零时(例如,在dec())并且仅在第二个计数变为零时调用析构函数。然后,以线程安全的方式完成所有这一切会增加很多微妙的复杂性,这将比这个答案花费更长的时间来解释。

另外,这部分的实现是错误的并且会泄漏内存:

void _check_delete_ptr()
{
    if (_obj_ptr == nullptr)
    {
        return;
    }

可以构造一个shared_ptr带有空指针,例如shared_ptr<int>((int*)nullptr),在这种情况下构造函数将分配一个控制块,但是因为_obj_ptr为 null 你将永远不会删除控制块。

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

Make_shared - 自己的实现 的相关文章

  • UTF8/UTF16 和 Base64 在编码方面有什么区别

    In c 我们可以使用下面的类来进行编码 System Text Encoding UTF8 System Text Encoding UTF16 System Text Encoding ASCII 为什么没有System Text En
  • 在 LINQ 查询中返回不带时间的日期

    我正在编写一个查询 我想计算按日期联系我们的呼叫中心的次数 看起来很简单 但由于联系日期字段是日期时间字段 我得到了时间 因此当我按联系日期 时间 分组时 每个联系日期实例的计数为 1 所以 我想只按日期分组 而不按时间分组 下面是我用来查
  • 模板类的不明确多重继承

    我有一个真实的情况 可以总结为以下示例 template lt typename ListenerType gt struct Notifier void add listener ListenerType struct TimeListe
  • 如何在C++中实现模板类协变?

    是否可以以这样一种方式实现类模板 如果模板参数相关 一个对象可以转换为另一个对象 这是一个展示这个想法的例子 当然它不会编译 struct Base struct Derived Base template
  • 使用 C# 在 WinRT 中获取可用磁盘空间

    DllImport kernel32 dll SetLastError true static extern bool GetDiskFreeSpaceEx string lpDirectoryName out ulong lpFreeBy
  • C# 用数组封送结构体

    假设我有一个类似于 public struct MyStruct public float a 我想用一些自定义数组大小实例化一个这样的结构 在本例中假设为 2 然后我将其封送到字节数组中 MyStruct s new MyStruct s
  • .Net Core / 控制台应用程序 / 配置 / XML

    我第一次尝试使用新的 ConfigurationBuilder 和选项模式进入 Net Core 库 这里有很多很好的例子 https docs asp net en latest fundamentals configuration ht
  • Windows 窗体不会在调试模式下显示

    我最近升级到 VS 2012 我有一组在 VS 2010 中编码的 UI 测试 我试图在 VS 2012 中启动它们 我有一个 Windows 窗体 在开始时显示使用 AssemblyInitialize 属性运行测试 我使用此表单允许用户
  • 我的 strlcpy 版本

    海湾合作委员会 4 4 4 c89 我的程序做了很多字符串处理 我不想使用 strncpy 因为它不会终止 我不能使用 strlcpy 因为它不可移植 只是几个问题 我怎样才能让我的函数正常运行 以确保它完全安全稳定 单元测试 这对于生产来
  • Windows 10 中 Qt 桌面应用程序的缩放不当

    我正在为 Windows 10 编写一个简单的 Qt Widgets Gui 应用程序 我使用的是 Qt 5 6 0 beta 版本 我遇到的问题是它根本无法缩放到我的 Surfacebook 的屏幕上 这有点难以判断 因为 SO 缩放了图
  • 像“1$”这样的位置参数如何与 printf() 一起使用?

    By man I find printf d width num and printf 2 1 d width num 是等价的 但在我看来 第二种风格应该与以下相同 printf d num width 然而通过测试似乎man是对的 为什
  • C 中的位移位

    如果与有符号整数对应的位模式右移 则 1 vacant bit will be filled by the sign bit 2 vacant bit will be filled by 0 3 The outcome is impleme
  • 将应用程序从 Microsoft Access 迁移到 VB 或 C#.NET

    我目前正试图说服管理层需要将我们的应用程序之一移植到 NET 该应用程序已经发展成为 Access 中的一个庞然大物 SQL 后端 拥有 700 个链接表 650 个表单 子表单 130 个模块和 850 个查询 我几乎知道这样做的所有主要
  • 在 URL 中发送之前对特殊字符进行百分比编码

    我需要传递特殊字符 如 等 Facebook Twitter 和此类社交网站的 URL 为此 我将这些字符替换为 URL 转义码 return valToEncode Replace 21 Replace 23 Replace 24 Rep
  • char指针或char变量的默认值是什么[重复]

    这个问题在这里已经有答案了 下面是我尝试打印 char 变量和指针的默认值 值的代码 但无法在控制台上看到它 它是否有默认值或只是无法读取 ASCII 范围 include
  • 已过时 - OpenCV 的错误模式

    我正在使用 OpenCV 1 进行一些图像处理 并且对 cvSetErrMode 函数 它是 CxCore 的一部分 感到困惑 OpenCV 具有三种错误模式 叶 调用错误处理程序后 程序终止 Parent 程序没有终止 但错误处理程序被调
  • ListDictionary 类是否有通用替代方案?

    我正在查看一些示例代码 其中他们使用了ListDictionary对象来存储少量数据 大约 5 10 个对象左右 但这个数字可能会随着时间的推移而改变 我使用此类的唯一问题是 与我所做的其他所有事情不同 它不是通用的 这意味着 如果我在这里
  • 在Linux中使用C/C++获取机器序列号和CPU ID

    在Linux系统中如何获取机器序列号和CPU ID 示例代码受到高度赞赏 Here http lxr linux no linux v2 6 39 arch x86 include asm processor h L173Linux 内核似
  • 窗体最大化时自动缩放子控件

    有没有办法在最大化屏幕或更改分辨率时使 Windows 窗体上的所有内容自动缩放 我发现手动缩放它是正确的 但是当切换分辨率时我每次都必须更改它 this AutoScaleDimensions new System Drawing Siz
  • 将 viewbag 从操作控制器传递到部分视图

    我有一个带有部分视图的 mvc 视图 控制器中有一个 ActionResult 方法 它将返回 PartialView 因此 我需要将 ViewBag 数据从 ActionResult 方法传递到 Partial View 这是我的控制器

随机推荐

  • 将数组的一部分向右移动的最快方法

    我需要在一个小数组中的给定索引处 插入 一个元素 即 将所有索引较大的元素向右移动 1 位 在 NET 中最快的方法是什么 注意 我添加了自己的答案 但我仍在寻找解释和更快的替代方案 编辑 我确实需要一个数组 而不是List
  • 为什么后台任务会阻止 SimpleHTTPServer 中的响应?

    我正在编写一个简单的基于浏览器的前端 它应该能够启动后台任务 然后从中获取进度 我希望浏览器收到一个响应 说明任务是否成功启动 然后轮询以确定任务何时完成 但是 后台任务的存在似乎会阻止立即发送 XMLHttpRequest 响应 因此我无
  • 无法在 Swift 中调用 Objective C 类方法

    我的项目是用 swift 语言编写的 我在项目中使用过可可豆荚 我使用可可豆荚将 HDAlerts 集成到我的项目中 HDAlerts位于objective c中 因此我在项目中添加了objective c桥接标头并在其中导入了Alerts
  • 我怎样才能让它工作。我尝试授予 sudo 权限

    我目前正在使用linux 并尝试使用 sudo 权限启动应用程序 但没有成功 我刚刚安装了 expo cli 并创建了新项目 当我启动应用程序时 表明 错误信息 Failed to construct transformer Error E
  • 使用 node.js 的服务器端 Mustache.js 示例

    我正在寻找一个使用的例子Mustachejs with Nodejs 这是我的例子 但它不起作用 Mustache未定义 我正在使用 master 分支的 Mustachejs var sys require sys var m requi
  • 无法解析符号 newRequestQueue - Volley

    我在 MainActivity 文件中有这段代码 RequestQueue queue new Volley newRequestQueue this 并且由于某种原因newRequestQueue部分是红色的 当我将鼠标悬停在它上面时 我
  • 表达与陈述

    我问的是关于 c 的问题 但我认为它在大多数其他语言中都是一样的 有谁有一个好的定义表达式 and 声明区别是什么 表达 评估某个值的东西 例子 1 2 x 陈述 执行某事的一行代码 例子 GOTO 100 在最早的通用编程语言 如 FOR
  • 即使 FetchMode = Join,Hibernate 也会发出 select 语句

    我有一个与国家 地区实体映射的 userAccount 实体 UserAccount 类中的国家 地区映射如下 ManyToOne fetch FetchType EAGER Fetch FetchMode JOIN JoinColumn
  • 在使用 window.open 打开的窗口中使用 document.write 编写的脚本元素不会在 Windows 7 上的 IE8 中执行

    我遇到了一个似乎只出现在 Windows 7 上的问题 它似乎在不同版本的 Windows 上的 IE8 中工作正常 基本上 我使用 window open 创建一个新窗口 然后使用 document write 写入该新窗口的内容 其中包
  • 如何在 Yii2 中的 ON 条件中使用常量 hasMany 关系

    我尝试创建一个多态关联 这在 Rails 中很常见 但不幸的是在 Yii2 中却不是 作为实现的一部分 我需要定义关系 public function getImages return this gt hasMany RecipeImage
  • IPv6:connect() 总是失败并显示 errno 22

    操作系统是Ubuntu 我正在对基本 IPv6 操作进行简单测试 PC通过集线器与IP Camera 支持IPv6 连接 ping6 测试成功 ping6 I eth1 fe80 240 8cff fe94 451e PING fe80 2
  • 在 Ruby 中从字符串中提取数字

    我正在使用这段代码 s line match ABCD d 4 values at 1 0 要从字符串中提取数字 例如 ABCD1234 ABCD1235 ABCD1236 etc 它有效 但我想知道在 Ruby 中我还有什么其他选择 My
  • JavaScript跨域调用:从HTTP到HTTPS的调用

    我需要对同一域的安全 HTTPS URL 进行异步调用 目前该页面正在使用常规 HTTP 非安全 换句话说 这是调用同一域中但使用 HTTPS 的 URL 在将此调用切换到 HTTPS 之前 我结束了实现服务器端代理以允许跨域 AJAX 调
  • 将 RTF 转换为纯文本

    我有一个 ERP 系统 它以 RTF 格式存储文本 我试图从中提取纯文本 我通过谷歌搜索并找到了类似的解决方案this one http webcheatsheet com php reading the clean text from r
  • 如何为 Dart 编写注释

    问题 实现注释的过程是什么 如何或者何时可以激活您开发的注释 I can t seem to find an example or tutorial on how to write a class to implement annotati
  • 如何使用 Java 为列表中的每个字符串添加或插入 ' (单引号),其中字符串以逗号分隔

    我有一个如下列表 url1 url2 url3 url4 该列表将基于 HTML 下拉列表中的多项选择 因此列表大小 即列表元素 会动态变化 我的问题是我无法获得将单引号添加到字符串的逻辑 我希望上面的字符串列表显示为 url1 url2
  • 将数据从一个表复制到另一个不同的列名

    我在将一个表的数据复制到另一个表时遇到问题 我有大约 100 个左右的单独表 它们通常具有相同的字段名称 但并非总是如此 我需要能够复制和映射字段 示例 源表是 BROWARD 并且具有列名称 broward ID name dob add
  • Ionic - 添加/删除phonegap-push-plugin - 未找到 CocoaPods

    我正在编写一个 Ionic 应用程序 但遇到了很多问题 特别是一个问题是 当我尝试使用以下命令添加phonegap push plugin时 sudo ionic plugin add phonegap plugin push 我收到以下错
  • Spring Boot 中如何划分 dev 和 prod 环境的 Liquibase 包结构?

    我的包结构如下 In db changelog db changelog master xml我包括 db changelog v1 db changelog 1 0 xml我还包括来自的所有变更日志 db changelog v1 cha
  • Make_shared - 自己的实现

    我正在尝试自己实现shared ptr 我有以下问题make shared 主要特点是std make shared它在连续的内存块中分配计数器块和对象 我怎样才能做同样的事情 我尝试做类似的事情 template