Warning: file_get_contents(/data/phpspider/zhask/data//catemap/6/cplusplus/149.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
C++ C++;通过共享的\u ptr类成员调用类的成员函数的安全习惯用法 问题描述_C++_C++11_Shared Ptr_Smart Pointers_Observer Pattern - Fatal编程技术网

C++ C++;通过共享的\u ptr类成员调用类的成员函数的安全习惯用法 问题描述

C++ C++;通过共享的\u ptr类成员调用类的成员函数的安全习惯用法 问题描述,c++,c++11,shared-ptr,smart-pointers,observer-pattern,C++,C++11,Shared Ptr,Smart Pointers,Observer Pattern,在为我的代码设计观察者模式时,我遇到了以下任务:我有一个类observer,它包含一个变量std::shared_ptr,我想对这个共享指针使用弱_ptr来安全地调用observer中的函数update()(有关更详细的动机,包括一些分析度量,请参见下面的编辑) 下面是一个示例代码: struct Receiver { void call_update_in_observer() { /* how to implement this function? */} }; struct Ob

在为我的代码设计观察者模式时,我遇到了以下任务:我有一个类
observer
,它包含一个变量
std::shared_ptr
,我想对这个共享指针使用
弱_ptr
来安全地调用
observer
中的函数
update()(有关更详细的动机,包括一些分析度量,请参见下面的编辑)

下面是一个示例代码:

struct Receiver
{
    void call_update_in_observer() { /* how to implement this function? */}
};

struct Observer
{
    virtual void update() = 0;
    std::shared_ptr<Receiver> receiver;
};
(仅供参考:
update()
的调用最多应该发生一次,因为它更新了某个派生类(即实际观察者)中的
shared\u ptr
。但是,调用一次或多次并不影响“安全性”问题。)

问题:

  • 为了以安全的方式执行该过程,
    观察者
    接收者
    的适当实现是什么


解决方案尝试 下面是一个最小实现的尝试——其思想是
Receiver
管理一组当前有效的
Observer
对象,其中一个成员被称为:

struct Receiver
{
    std::set<Observer *> obs;
    void call_update_in_observer() const
    {
        for(auto& o : obs)
        {
            o->update();
            break;  //one call is sufficient
        }
    }
};

问题:

  • 这已经是安全的了吗?--“安全”意味着没有调用过期的
    Foo
    对象。或者是否存在一些必须被忽略的陷阱 考虑过吗

  • 如果这段代码是安全的,那么如何实现move构造函数和赋值呢

(我知道这有一种适合CodeReview的感觉,但它更像是关于这个任务的合理模式,而不是关于我的代码,所以我在这里发布了它……而且移动构造函数仍然缺失。)



编辑:动机

由于上述要求在评论中被称为“混淆”(我不能否认),这里的动机是:考虑一个自定义<代码>向量<代码>类,为了节省内存,执行浅拷贝:

struct Vector
{
    auto operator[](int i) const { return v[i]; }
    std::shared_ptr<std::vector<double> > v;
};
在此设置中,当向量表达式
v
在程序中的某个地方被修改时,需要将该更改传播到从属
Average
类(其中可以存在多个副本),以便重新计算
存储
,否则它将包含错误的值。但是,在此更新过程中,
存储
只需重新计算一次,而不管
平均
对象存在多少个副本


这种共享指针和值语义的混合是我在上述混乱的情况下运行的原因。我的解决方案是在观察者中执行与更新对象相同的基数,这就是 SydDypPTR <代码>的原因。

是否考虑多线程?你的设计所代表的似乎是…混乱的。如果
Bar
Foo
的某种管理者,为什么它不存在于它所管理的
Foo
之外?也就是说,为什么管理者
共享他们管理的对象的成员?为什么允许
Foo
之外的代码声明经理的所有权(毕竟,你把
Foo::bar
公开了
)?这里的设计对我来说没有多大意义。@Jarod42:到目前为止还没有多线程。但也许以后会有。@Nicolas关于不寻常的所有权有什么看法?还有:你为什么要叫
Foo->update()<代码> >代码> >更新>代码>是一个虚拟函数。你甚至不知道你会调用哪个版本。@尼科尔博拉斯:我为这个问题增加了一个设计的动机,希望能消除混乱。对于私有/公共成员,我不关心最小的实现。你认为多线程还是不多线程?我想。您的设计所代表的关系似乎…混乱。如果
Bar
Foo
的某种管理者,为什么它不存在于它所管理的
Foo
s之外?也就是说,为什么管理者
共享他们管理的对象的成员?为什么代码在
Foo 允许声明管理者的所有权(毕竟,你将
Foo::bar
公开了
)?这里的设计对我来说没有多大意义。@Jarod42:到目前为止没有多线程处理。但可能以后会有。@nicobolas关于非常不寻常的所有权以及:你为什么要调用
Foo->update()
只有一次?
update
是一个虚拟函数。你甚至不知道要调用哪个版本。@Nicolas:我在问题中添加了设计动机,希望能消除混淆。关于私有/公共成员资格,我不关心最小的实现。
struct Observer
{
    Observer()
    {
        receiver->obs.insert(this);
    }

    Observer(Observer const& other) : receiver(other.receiver)
    {
        receiver->obs.insert(this);
    }

    Observer& operator=(Observer rhs)
    {
        std::swap(*this, rhs);
        return *this;
    }

    ~Observer()
    {
        receiver->obs.erase(this);
    }

    virtual void update() = 0;

    std::shared_ptr<Receiver> receiver = std::make_shared<Receiver>();
};
struct Vector
{
    auto operator[](int i) const { return v[i]; }
    std::shared_ptr<std::vector<double> > v;
};
template<typename _VectorType1, typename _VectorType2>
struct VectorSum
{
    using VectorType1 = std::decay_t<_VectorType1>;
    using VectorType2 = std::decay_t<_VectorType2>;

    //alternative 1: store by value
    VectorType1 v1;
    VectorType2 v2;

    //alternative 2: store by shared_ptr
    std::shared_ptr<VectorType1> v1;
    std::shared_ptr<VectorType2> v2;

    auto operator[](int i) const
    {
        return v1[i] + v2[i];
    }
};

//next overload operator+ etc.
type                      Average access time        ratio
--------------------------------------------------------------
Foo                     : 2.81e-05                   100%
std::shared_ptr<Foo>    : 0.000166                   591%
std::unique_ptr<Foo>    : 0.000167                   595%
std::shared_ptr<FooBase>: 0.000171                   611%
std::unique_ptr<FooBase>: 0.000171                   611%
template<typename _VectorType>
struct Average
{
    using VectorType = std::decay_t<_VectorType>;

    VectorType v;
    std::shared_ptr<std::vector<double> > store;

    auto operator[](int i) const
    {
        //if store[i] is filled, return it
        //otherwise calculate average and store it.
    }
};