一、何为强,何为弱?
所谓强、弱只是相对而言的,强者制人而弱者受制于人。强引用控制着原对象的生和死,只要有一个强引用对象指向了原对象,那么原对象就不会被析构,当所有指向原对象的强引用都因为离开作用域而自然析构的时候,原对象的生命周期也就结束了,因为没有人能找到它了,理所当然,它的死期也就到了,这时强引用会通知编译器将他kill掉,否则,就会违背自然规律,发生内存泄露;而弱引用恰恰相反,弱引用所指向的原对象不但不受弱引用控制,所有弱引用的使用反而还受原对象的限制,弱引用并不对引用进行计数,而是当原对象析构时,发出通知,告诉所有弱引用,“你们都不要忙活了,你们的老窝已经没了“,这时弱引用依然存在,只是它们都成废物了,等着越过作用域这条”奈何桥“,也就彻底化为灰烬了,等待重生。这时我们看到了强引用的”专横跋扈“,掌握着生杀大权,同时也能够看到弱引用的”软弱无能“,任人宰割。这就是所谓的,软的(弱引用)怕硬的(源对象),硬的怕不要命的(强引用)。这样比喻是还是比较恰当,因为弱引用本身就生活在”栈“里,生死有命,又无权操作原对象,只能当原对象的傀儡;强引用虽然同样也是生活在”栈“中,但是在他们死的时候,会拉上原对象陪葬。
二、遇强则强
强引用虽然可以控制原对象的生死,而自己却无法摆脱被析构的命运。天罗地网固然疏而不漏,但任何事物都会存在弱点,强引用在成千上万次的转世轮回中,终于悟出长生不老之术。于是,他就找了另外一个强引用作为合作”伙伴“,他让另外一个强引用躲进他所指向的对象中,而他自己躲入了这个强引用所指向的对象。这时奇迹便发生了,只要这两个强引用所指向的对象不析构,那么这两个强引用永远也不会析构,因为他们都是对象中的成员,反过来依然成立,只要这两个强引用不被析构,那么两个对象也不会析构,这时两个强引用发生了死锁,这两块内存永远也不会被释放,直到应用程序的结束。
class A: public ReferenceCountObject
{
public:
ReferenceCountObjectPtr<B> m_b;
JUCE_LEAK_DETECTOR(A)
};
class B: public ReferenceCountObject
{
ReferenceCountObjectPtr<A> m_a;
JUCE_LEAK_DETECTOR(B)
};
void main()
{
ReferenceCountObjectPtr<A> a = new ReferenceCountObjectPtr<A>();
ReferenceCountObjectPtr<B> b = new ReferenceCountObjectPtr<B>();
a->m_b = b;
b->m_a = a;
}
比如以上程序 ,待程序运行完毕,会提示类A和类B分别有一个对象没有被回收。然而这并不是我们想要的,我们的最终目的是让他们符合自然规律,有生便会有死,如果放任他们“与天地同寿”,反而是没有必要的浪费,甚至发生危险。可以想象如果地球上的人只生不死的话,数年之后是什么后果。
三、强弱互补
为了防止两个强引用“强强联手”,作为“造物者”,我们不能像上面那样,让两个强引用去结合。这时“百无一用”的弱引用遍派上了用场,强弱结合便不会发生死锁。
class Man: public ReferenceCountObject
{
public:
WeakReference<Woman> myWife; //如果myWife所指向的Woman对象析构,myWife便不能再使用
//反之,Man对象析构,不会对myWife所指向的对象造成任何影响
void DoHisWork()
{
}
JUCE_LEAK_DETECTOR(Man)
};
class Woman: public ReferenceCountObject
{
ReferenceCountObjectPtr<Man> myHasband; //如果这个是Man的唯一引用,那么Wonman对象析构,Man也会随之析构,当然如果其他地方还有Man的引用,Man便不会析构
//反之,如果Woman不析构,myHasband所指向的对象永远也不会析构
WeakReference<Woman>::Master masterReference;
friend class WeakReference<Woman>;
void DoHerWork()
{
}
JUCE_LEAK_DETECTOR(Woman)
};
ReferenceCountObjectPtr<Man> fun()
{
ReferenceCountObjectPtr<Man> man = new ReferenceCountObjectPtr<Man>();
ReferenceCountObjectPtr<Woman> woman = new ReferenceCountObjectPtr<Woman>();
man->myWife = woman;
woman->myHasband = man;
woman->myHasband->DoHiswork();
if (!man->myWife.wasObjectDeleted())
{
ReferenceCountObjectPtr<Woman> woman = man->myWife; //这里注意弱引用不要直接使用,要转化为强引用
woman->DoHerwork(); //因为弱引用所指向的对象指不定什么时候被析构,转化为强引用,使用才安全
}
return man;
}
void main()
{
ReferenceCountObjectPtr<Man> man = fun();
if (!man->myWife.wasObjectDeleted())
{
ReferenceCountObjectPtr<Woman> woman = man->myWife; //这里注意弱引用不要直接使用,要转化为强引用
woman->DoHerwork(); //因为弱引用所指向的对象指不定什么时候被析构,转化为强引用,使用才安全
}else
{
printf("man->myWife已经析构");
}
ReferenceCountObjectPtr<Woman> woman2 = new ReferenceCountObjectPtr<Woman>();
man->myWife = woman2;
woman2->myHasband = man;
}
这段代码,看起来比较乱,但是把它以故事的形式来读的话,反而更好理解。在函数fun()里,man和woman同时出生,然后“结婚”,结婚的约定是,woman保留man的强引用,man保留woman的弱引用,这样,有woman存在man不会死,而woman死时不会影响man,只会默默地留下遗言“我已经死了,不要想我了,你要好好活下去!”,反之,没有反之了,man不会比woman先死。于是他俩快乐的生活着,然而现实是残酷的,fun()函数只能有一个返回值,如果返回woman,那他俩都不会死,因为woman会带着man冲出fun()的作用域,如果返回man,那么woman就死定了,man保留的是woman的弱引用,没有能力将woman带出fun()函数。最后fun()函数将man返回,这时woman就不在了,于是man又和woman2“结婚”继续过完自己的一生。
现实中我们讲究男女平等,但是在程序中要分清主次,两个类相结合的话,必然一主一次,孰强孰弱视情况而定。比如,root和node,item和list,都很好区分。