C++ 当一个对象';他的死使另一个物体生病

C++ 当一个对象';他的死使另一个物体生病,c++,design-patterns,encapsulation,C++,Design Patterns,Encapsulation,当对象b在内部引用并使用它不拥有的对象a时,a的死亡会使b生病。下面是一个简单的例子来说明这一点: #include <iostream> const int my_int = 5; class A { private: int n_; public: int n() const { return n_; } A(int); }; A::A(int n__) : n_(n__) {} class B { private: const

当对象
b
在内部引用并使用它不拥有的对象
a
时,
a
的死亡会使
b
生病。下面是一个简单的例子来说明这一点:

#include <iostream>

const int my_int = 5;

class A {
  private:
    int n_;
  public:
    int n() const { return n_; }
    A(int);
};

A::A(int n__) : n_(n__) {}

class B {
  private:
    const A *ap_;
  public:
    int m() const { return 1 + ap_->n(); }
    explicit B(const A *);
};

B::B(const A *const ap__) : ap_(ap__) {}

int main()
{
    std::cout << "Will put an unnamed A on the heap.\n";
    A *const p = new A(my_int);
    std::cout << "Have put an unnamed A on the heap.\n";
    std::cout << "p->n() == " << p->n() << "\n";
    B b(p);
    std::cout << "b. m() == " << b. m() << "\n";
    std::cout << "Will delete  the unnamed A from the heap.\n";
    delete p;
    std::cout << "Have deleted the unnamed A from the heap.\n";
    std::cout << "b. m() == " << b. m() << "\n"; // error
    return 0;
}
但是,在您的计算机上,结果可能是SEGFULT或谁知道是什么

我的示例的问题似乎在于,该示例间接破坏了
b
的封装,让程序员记住
b
的持续有效性取决于
a
的持续存在。当程序员忘记时,程序就会中断。因此,无论何时处理
a
,被骚扰的程序员都必须记住
b
,即使类型a本身并不关心类型b。正如您所知,面向对象的程序员如果可以帮助的话,他们更愿意不必记住这些琐事

在编程时,我偶尔会在更复杂的伪装下遇到这个问题。我今天又遇到了它。有人认为,不知何故,应该存在一种优雅的设计模式来维护
b
的正确封装,将记忆
b
a
存在的依赖性的责任从程序员转移到编译器,并且该模式基本上应该涉及一些比智能指针和全面引用计数更不复杂的东西。然而,也许我错了。也许这正是它们引用计数的智能指针的用途。无论哪种方式,我既不知道应用于解决问题的正确模式,也不知道修复代码的最佳方法

如果你知道,你会告诉我吗

这是我在Stackoverflow上注意到的最相关的答案;但是,除了用一两个我不懂的词外,这个答案似乎并没有回答这个问题

(我的编译器仍然不能很好地支持C++11,但是如果C++11提供了一个专门用来解决我的问题的功能,那么我当然有兴趣了解它。不过,无可否认,我的问题主要是关于OO/范围界定的基础知识。这个问题对底层设计模式的兴趣甚至比这个或那个更大这是最新编译器的新功能。)

读者注意事项

一些好的答案使这个问题变得更加优雅。你知道,在Stackoverflow上,提问者有责任接受最好的答案,这样(当你在几个月或几年后阅读此文章时)你就不必搜索它了

然而,这两个答案的结合才是这个问题的最佳答案。你应该阅读这两个答案:

  • @MatthieuM的再分享所有权和观察者模式;以及
  • @JamesKanze的re why和when observer模式可能是首选

    • 如果您真的需要只保留一个对象,而不是多个副本,那么智能解决方案就是使用智能指针

      C++11为您提供了以下选项:

      • std::unique\u ptr
      • std::shared\u ptr
      • std::弱\u ptr

      在你的情况下,我认为你需要第二个:
      std::shared_ptr

      如果你真的需要只保留一个对象,而不是多个副本,那么智能解决方案就是使用智能指针

      C++11为您提供了以下选项:

      • std::unique\u ptr
      • std::shared\u ptr
      • std::弱\u ptr

      在你的例子中,我认为你需要第二个:
      std::shared_ptr

      你要寻找的模式是“如果你拥有一个对象,不要放弃对它的非所有权引用,然后销毁它。”我不认为它有更好的名字,但它确实是一个很好的C++礼仪。如果你给一个可以保持它的对象的指针,你需要知道这个对象可能依赖于指针保持有效。在这种情况下,你只需确保你的对象的寿命超过非拥有对象的生命周期。 如果你正在传递一个函数,你可以假设这个函数没有使用全局状态,那么我就不担心它了——一旦函数返回,你应该假设它是用那个指针完成的

      正如纳瓦兹所说,停止担心这一点的真正方法是使用具有适当所有权语义的智能指针。如果你想将所有权转移到另一个对象,请使用
      std::unique_ptr
      。如果你想共享所有权,请使用
      std::shared_ptr
      。如果你想使用不拥有的原始指针,我不会说不要,b但至少要知道它可能导致的问题


      为了避免过早地意外删除对象,可以执行以下操作:

      const std::unique_ptr<A> p(new A(my_int));
      B b(p.get());
      // the A object will be destroyed at the end of the current scope
      
      const std::unique_ptr p(新的A(我的int));
      B(p.get());
      //对象将在当前范围结束时销毁
      

      在这里,您没有
      std::shared_ptr
      的开销(非常小!)。我不确定我是否真的会推荐它。完全智能比半智能更可取。
      const std::unique_ptr
      具有与
      boost::scoped_ptr
      类似的所有权语义:它说“我拥有它,没有人会拥有它。”不同之处在于,您无法在此
      const std::unique\u ptr上调用
      reset
      ,就像在
      boost::scoped\u ptr上调用
      reset

      一样。您要寻找的模式是“如果您拥有一个对象,不要放弃对它的非拥有引用,然后销毁它。”我不认为它有更好的名字,但它确实是一个很好的C++礼仪。如果你给一个可以保持它的对象的指针,你需要知道这个对象可能依赖于指针保持有效。在这种情况下,你只需确保你的对象的寿命超过非拥有对象的生命周期。 如果您要传递到一个函数,并且您可以假设该函数没有使用全局状态,那么
      const std::unique_ptr<A> p(new A(my_int));
      B b(p.get());
      // the A object will be destroyed at the end of the current scope