C++ 观察器设计模式,防止对象被重新分配

C++ Observer design pattern, prevent object from being reallocated

提问人:yonutix 提问时间:10/24/2023 最后编辑:yonutix 更新时间:10/28/2023 访问量:140

问:

由于在某些情况下对象被重新分配,例如在 std::vector 的情况下,C++ 可观察模式实现可能会有问题:

根据“四人帮”一书,可观察对象的实现如下所示:

class ObservableSubject {
public:
  virtual ~ObservableSubject() = default;
  virtual void notify() = 0;

protected:
  ObservableSubject() = default;

private:
  std::vector<Observer*> mObservers;
};

其中 Observer 如下所示:

class Observer {
public:
  virtual ~Observer() = default;

  virtual void update(ObservableSubject& subject) = 0;

protected:
  Observer() = default;
};

考虑这个例子:

class Mapper final: public Observer  {};

当 Mapper 被这样持有时,一旦重新分配内存,持有的指针就会失效。 解决方案当然是使用指针(即 )并具有 .此解决方案可能会有问题,因为开发人员必须记住 Mapper 不能重新分配。std::vector<Mapper>mObserversstd::vector<Mapper>std::shared_ptrstd::vector<std::shared_ptr<Mapper>>

有没有办法约束 C++ 中的类不被重新分配?

书中的实现:enter image description here enter image description here

C++ 设计模式 17 20 C++23

评论

0赞 Peter 10/24/2023
您认为调整大小将重新分配对象(由向量元素指向)的信念/断言是不正确的。std::vector<Observer *>
0赞 yonutix 10/24/2023
假设 std::vector<Mapper> 将重新分配。我添加了一个编辑来澄清。
1赞 Peter 10/24/2023
是的,它会的。但它会重新分配一个(动态分配的)数组,并且作为重新分配的一部分,在释放原始数组之前将现有值复制到重新分配的数组中。但是,按值复制一个指针(或一组指针)不会影响这些指针指向的对象。Observer *
2赞 HolyBlackCat 10/24/2023
如果GoF这样写,他们就不擅长C++。至少,当有人尝试复制/移动此类时,您应该使用复制构造函数和复制赋值来获得编译错误。但是,更理想的做法是实现有意义的移动构造函数和移动赋值,以更新指向观察者的指针,无论它存储在何处。class ObservableSubject=delete
3赞 Daniel Langr 10/24/2023
C++ 中的对象永远不会重新分配,从某种意义上说,它们会从一个存储转移到另一个存储。重新分配中的向量通过调用 move 或 copy 构造函数创建新元素(加上原始元素被破坏)。如果要避免这种情况,可以同时禁用两者。

答:

1赞 Jarod42 10/24/2023 #1

您可以禁止构造函数并创建工厂返回智能指针:

class Mapper final: public Observer  {
     // Pass-key idiom to allow `std::make_unique` usage
     class private_key {
         private_key() = default;
     };
public:
    Mapper(private_key) {}

    Mapper(const Mapper&) = delete;
    Mapper(Mapper&&) = delete;

    static std::unique_ptr<Mapper> create() { return std::make_unique<Mapper>(private_key{}); }

    // ...
};

所以对象的地址是“稳定的”。