关于工作和读书的笔记
[原创文章欢迎转载,但请保留作者信息]
Justin 于 2009-11-08
其实是说不要在构造/析构函数里调用虚函数。
道理也是不难但也不那么明显:虚函数拥有虚函数的类就有虚表,虚表可能会引发子类相应虚函数的调用,在这些调用中有可能对某些子类对象成员的访问。在构造一个子类对象的时候可以分成这几步:开始构造父类部分 -> 完成父类部分并开始构造子类部分 -> 完成子类部分(完成整个构造工作)析构一个子类对象的时候:开始析构子类部分 -> 子类析构完毕并开始析构父类部分 -> 完成析构父类部分(完成整个析构工作)在构造函数的第一步,子类对象成员还不存在,调用虚函数有可能会访问不存在的子类成员;哪怕到了第二步,因为子类对象还没有完全构造完毕,此时调用虚函数也是危险的。事实上在整个构造过程中,该对象都被视作一个父类对象。(不是我说的,是Scott说的)反过来也是同样道理,在析构函数的第一步,子类成员已经开始销毁,不能调用虚函数;到了第二步,整个子类部分都没有了,更不能用虚函数了。而在整个析构过程中,该对象也是被看作是父类对象的。
大师说的是道理,我大概只能联想。
一个模特的工作是穿上服装拍照。拍照这个技术活可以看作是这个对象的一个虚函数。在女模的一天之始,美丽动人的她需要先起床(基类构造),习惯裸睡的她还需要到更衣室去穿衣服(子类构造)。然后她对摄影师说“准备好啦”(构造完毕),就开始一天的工作:被拍。到了晚上女模回家(开始析构),先要卸掉身上的束缚(子类析构),最后上床(基类析构),当然,是不是一个人上床不属于我联想的范围。作为拍照这个虚函数来说,女模睡觉的时候是不能做的,这个时候她还没有摆姿势的意识;女模起床以后也是不能拍的,因为衣服还没穿上(OK……我承认这个例子不是很好);穿好衣服,她说“准备好啦”,拍照就可以开始了。同样道理,最迟你也只能在她晚上说拜拜之前拍完,到回家开始脱衣服睡觉觉之后,你就要等到明天再工作了。
然而问题往往没有那么简单明了:很多时候一个构造/析构函数里会调用一些普通的函数,而这些函数又可能间接地调用了虚函数。在这种情况下,傻大粗的编译器不会报错。到了灾难发生时只有程序员可怜兮兮地抓臭虫……那么有没有办法能够确保虚函数不会在对象构造/析构过程中被调用呢?别看我,大师开讲了:方法之一就是用参数传递代替虚函数机制。把可能被构造函数调用的虚函数改成非虚函数,然后让父类的构造函数将需要的信息/数据通过参数传递给子类构造函数。对于析构函数,做法也类似。码了那么多字,要不,来段代码消化消化?
Copyright @ Justin.H Powered by: .Text and ASP.NET Theme by: .NET Monster