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.
}
};