当一个对象在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;
}
当然,可以通过b
保留副本a
而不是指向它的指针来解决此问题,但假设它b
不希望保留副本(因为a
占用大量内存或出于其他原因)。假设b
更喜欢仅引用现有的a
. 当a
悄悄地死去时,b
永远不会注意到。然后,当b
尝试使用时a
,会产生不可预测的行为。
在我的电脑上,该示例的输出恰好是这样的:
Will put an unnamed A on the heap.
Have put an unnamed A on the heap.
p->n() == 5
b. m() == 6
Will delete the unnamed A from the heap.
Have deleted the unnamed A from the heap.
b. m() == 1
但是,在您的计算机上,结果可能是段错误或谁知道。
我的示例的问题似乎在于示例间接打破了 的封装b
,让程序员记住 的持续有效性b
取决于 的持续存在a
。当程序员忘记时,程序就会中断。因此,即使类型 A 本身并不关心类型 B ,受骚扰的程序员b
在工作时也必须牢记。如您所知,面向对象的程序员宁愿不必记住这些琐事,如果他们可以提供帮助的话。a
我在编程时时不时地以更复杂的形式遇到这个问题。我今天又遇到了。有人认为,不知何故,应该存在一种优雅的设计模式来维护对 的适当封装,将记住 对 存在的依赖b
的责任从程序员转移到编译器——并且该模式从根本上应该包含更少的东西比智能指针和成熟的引用计数更详细。然而,也许我错了。也许这正是它们引用计数的智能指针的用途。无论哪种方式,我都不知道解决问题的正确模式,也不知道修复代码的最佳方法。b
a
如果你知道,你会告诉它吗?
这是我在 Stackoverflow 上注意到的最相关的答案;但是,除了使用我不理解的一两个词之外,无论如何,那个答案似乎并没有回答这个问题。
(我的编译器仍然不能很好地支持 C++11,但是如果 C++11 带来了专门用于解决我的问题的功能,那么我当然应该有兴趣了解它。但是,诚然,我的问题主要是关注 OO/作用域基础。这个问题对底层设计模式甚至比对最新编译器的这个或那个新特性更感兴趣。)
读者须知
一些很好的答案为这个问题增光添彩。如您所知,在 Stackoverflow 上,提问者有责任接受最佳答案,以便(当您在数月或数年后阅读此书时)您不必搜索它。
然而,最能回答这个问题的是两个答案的组合。你应该同时阅读: