如何使用weak_ptr强制执行单一所有权? (或者,如何子类shared_ptr和重写dtor) [英] How can I enforce single ownership with weak_ptr? (Or, how to subclass shared_ptr and override dtor)

查看:116
本文介绍了如何使用weak_ptr强制执行单一所有权? (或者,如何子类shared_ptr和重写dtor)的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

因此,我已经做了(少量)阅读,并且知道与原始指针结合使用的unique_ptr 是建模唯一所有权时要使用的模式。



但是,我真的很喜欢简单而清晰的概念,即使用weak_ptr来检查值是否有效,然后在使用完后丢弃shared_ptr,这样可以使所有人



我现在的特殊问题是创建一个富有表现力且灵活的系统来跟踪多点触摸,使用起来似乎很优雅代表触摸的对象的破坏是触摸已结束的信号。如果我使用原始指针路由,则需要定义一些与该系统接口的每个组件都需要遵循的语义,这有点丑陋,例如涉及第二个参数来指示指针是否有效。
带有原始指针路由的问题可能是个草率的问题,因为我不希望这成为一个大项目,但是在如何编写最佳现代C ++代码方面,这个问题主要具有实际意义。 / p>

伪代码:

  class InputConsumer {
void handle( std :: list< std :: weak_ptr< Touch>> *);
//消费者不持有对其关注之外的任何事物的引用。
//它只需要知道如何处理提供给它的输入数据。
//消费者是一个孩子,可以给他玩玩具,而我正在尝试
// //看看我能把它放到沙箱里多远
}
class InputSender {
std :: list< std :: weak_ptr< Touch>>暴露的输入数据;
std :: list< std :: shared_ptr< Touch>>实际接触;
//发送输入事件时,发件人将填充暴露的输入数据。
//我想让消费者尽可能多地复制weak_ptrs,
//但永远不要保留它无限期地。似乎没有
//是执行此操作的简便方法(诚然这有点含糊。它
//必须存在足够长的时间才能用于读取数据,但是
//不是例如3帧。也许我需要做一个智能的
//里面有一个计时器的智能指针。)
std :: list< std :: weak_ptr< InputConsumer>>消费者;
void feedConsumersWithInput(){
for(auto i = consumers.begin(); i!= consummers.end(); ++ i){
if(i-> expired( )){
consumers.erase(i);
} else {
i-> lock()-> handle(& exposedinputdata);
}
}
}

当我看到 weak_ptr 表示与我正在建模的语义非常相似的语义,我只是真的想使用它,因为它的界面简洁明了,最重要的是它自我记录了如何代码将起作用。



现在,我很确定,直到 InputConsumer之前,一切都会变得非常桃红色 code>在 weak_ptr< Touch> 上调用 lock()并保留 shared_ptr< ;触摸> 。即使它的主要所有者删除了自己的 shared_ptr ,它也会阻止底层的 Touch 被释放!在我看来,这是唯一的皱纹,而在那一点上。我认为,使用shared_ptr加强所有权处理比使用原始指针要困难得多。



有哪些方法可以对此进行修补?我在考虑也许要使weak_ptr的模板子类(?!我从来没有写过这样的东西,最近才进入模板。喜欢它们)会以某种方式禁止保留shared_ptr或其他东西。



也许我可以将 shared_ptr 子类化,并在不调用删除程序的情况下覆盖其dtor以抛出该异常?

解决方案

考虑到拥有 weak_ptr 总是需要引用计数,推出任何解决方案(或多或少),例如重写 shared_ptr 一个。



快速而肮脏的方法可能是派生shared_ptr,并仅向其提供移动ctor( monitore_ptr(monitored_ptr&&))和转移运算符( monitored_ptr& == monitored_ptr&&))禁用 shared_ptr 复制(并因此禁用 共享)功能。



问题的推导是 shared_ptr 不是polymo rphic,您最终得到一个非多态类型,该多态类型对shared_ptr表现出某些多态性(您可以将其分配给它,从而违反了假设)。



保护继承并仅公开必需的功能(基本上是 * ->运算符)。



要避免针对 weak_ptr 的错误行为(例如您给 monitored_ptr weak_ptr 赋予 shared_ptr )...我也建议覆盖 weak_ptr

在那一点上,您最终得到一对自足且与任何其他共享指针不兼容的类



无论如何,关键是编写适当的构造函数,而不是(如您所建议的)抛出析构函数:这是一种潜在的陷阱,



(例如,参见此处


So, I've done (a small amount) of reading and am aware that unique_ptr in combination with raw pointers is the pattern to use when modeling unique ownership.

However, I really like the simple and clear concept of using a weak_ptr to check if a value is valid, and then discard the shared_ptr after using it, and this keeps everyone happy (at a slight reference counting performance cost).

My particular problem right now is in creating an expressive and flexible system for tracking multitouch points, and it seemed elegant to use the destruction of the object that represents a touch to be the signal that the touch has ended. If I went with the raw pointer route, I would need to define some semantics that each component interfacing with this system would need to conform to, something slightly ugly like having a second argument involved that indicates whether the pointer is valid or some such. This issue with the raw pointer route is perhaps a strawman issue as I don't expect this to become a large project, but the question is mostly of practical interest in terms of how to write the best modern C++ code.

pseudocode:

class InputConsumer {
    void handle(std::list<std::weak_ptr<Touch>>*);
    // consumer doesnt hold references to anything outside of its concern. 
    // It only has to know how to deal with input data made available to it.
    // the consumer is a child who is given toys to play with and I am trying to
    // see how far I can go to sandbox it
}
class InputSender {
    std::list<std::weak_ptr<Touch>> exposedinputdata;
    std::list<std::shared_ptr<Touch>> therealownedtouches;
    // sender populates exposedinputdata when input events come in.
    // I want to let the consumer copy out weak_ptrs as much as it wants,
    // but for it to never hold on to it indefinitely. There does not appear
    // to be an easy way to enforce this (admittedly it is kind of vague. it
    // has to be around for long enough to be used to read out data, but
    // not e.g. 3 frames. Maybe what I need is to make an intelligent
    // smart pointer that has a timer inside of it.)
    std::list<std::weak_ptr<InputConsumer>> consumers;
    void feedConsumersWithInput() {
        for (auto i = consumers.begin(); i != consumers.end(); ++i) {
        if (i->expired()) {
            consumers.erase(i);
        } else {
            i->lock()->handle(&exposedinputdata);
        }
    }
}

When I saw the ability of weak_ptr to express very similar semantics to what I am modeling, I just really wanted to use it, because its interface is clean and simple, and most importantly it self-documents how this code is going to work. This is a huge benefit down the road.

Now I'm pretty sure that everything will be really peachy until such time as an InputConsumer calls lock() on weak_ptr<Touch> and retains the shared_ptr<Touch>. It will prevent the underlying Touch from being freed even after the primary owner of it has erased its owning shared_ptr! This seems to me the only wrinkle, and a little one at that. I think it's far harder to screw up ownership handling with shared_ptr than it is to do so with raw pointers.

What are some ways of patching this up? I am thinking of maybe making a template subclass (?! I have never written such a thing, recently got into templates. Loving them) of weak_ptr that will somehow forbid retaining a shared_ptr, or something.

Maybe I can subclass shared_ptr and override its dtor to throw if it doesn't call the deleter?

解决方案

Considering that having a weak_ptr always requires reference counting, rolling out whatever solution is (more or less) like rewriting the shared_ptr one.

The quick and dirty way is probably derive shared_ptr and provide it with only the move ctor (monitore_ptr(monitored_ptr&&) ) and transfer operator (monitored_ptr& operator=(monitored_ptr&&) ), thus disabling the shared_ptr copy (and hence "sharing") capabilities.

The problem of derivation is that, being shared_ptr not polymorphic, you end up with a non polymorphic type that exibit some polymorphism towards shared_ptr (you can assign to it, thus violating your assumptions).

This can be compensated by using protected inheritance and re-expose only the required functionalities (essentially the * and -> operators).

To avoid miss-behavior against weak_ptr (like your monitored_ptr given to weak_ptr given to shared_ptr)... I'll also suggest to override weak_ptr as well, with protected inheritance.

At that point you end up with a pair of classes that are self sufficient and not compatible with any other shared pointer.

In any case, the key is writing proper contructors, and not (as you proposed) throw in the destructor: it is a situation with a lot of potential gotcha, hardly manageable.

(see for example here)

这篇关于如何使用weak_ptr强制执行单一所有权? (或者,如何子类shared_ptr和重写dtor)的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆