<<More Effective C++>>读书笔记5: 技巧(2)

来源:互联网 发布:大连理工网络教育 编辑:程序博客网 时间:2024/05/19 04:28
   《More Effective C++》+《Effective C++》,两本经典双剑合璧,必然威力无穷。
   
   《More Effective C++》的第5章约占全书的45%,是全书最难的部分,有些地方看的人很晕,但大多会让人心悦诚服;大部分的技巧都不局限于C++,可应用于所有语言的编程思想。
    为了便于消化,我将这一章分为两部分来阅读。      

    Item M29:引用计数
1. 引用计数是这样一个技巧,它允许多个有相同值的对象共享这个值的实现。这个技巧有两个常用动机: 
   1) 简化跟踪堆中的对象的过程。一旦一个对象通过调用new 被分配出来,最要紧的就是记录谁拥有这个对象,因为只有其所有者负责对这个对象调用delete。
      但是,所有权可以被从一个对象传递到另外一个对象(例如通过传递指针型参数),所以跟踪一个对象的所有权是很困难的;像auto_ptr 这样的类可以帮助我们,但经验显示大部分程序还不能正确地得到这样的类。
 引用计数可以免除跟踪对象所有权的担子,因为当使用引用计数后,对象自己拥有自己。当没人再使用它时,它自己自动销毁自己。因此,引用计数是个简单的垃圾回收体系。
   2) 节省内存,而且可以使得程序运行更快,因为不需要构造和析构这个值的拷贝。
2. 是实现“引用计数”开始之前,认识到“我们需要一个地方来存储这个计数值”是很重要的。这个地方不能在String 对象内部,因为需要的是每个String值一个引用计数值,而不是每个String 对象一个引用计数。这意味着String 值和引用计数间是一一对应的关系,所以我们将创建一个类来保存引用计数及其跟踪的值。
   我们叫这个类StringValue,又因为它唯一的用处就是帮助我们实现String 类,所以我们将它嵌套在String 类的私有区内。另外,为了便于Sting 的所有成员函数读取其数据区,我们将StringValue 申明为struct。需要知道的是:将一个struct 内嵌在类的私有区内,能便于这个类的所有成员访问这个结构,但阻止了其它任何人对它的访问(当然,除了友元)。
   [我想象中的一个地方来存储这个计数值的地方是static变量;这里是String内部的堆内存,某一对象销毁,其它对象还可以指向这块内存]        
3. "与其它对象共享一个值直到写操作时才拥有自己的拷贝”的想法在计算机科学中已经有了悠久而著名的历史了,尤其是在操作系统中:进程共享内存页直到它们想在自己的页拷贝中修改数据为止。这个技巧如此常用,以至于有一个名字:写时拷贝。它是提高效率的一个更通用方法--Lazy 原则--的特例。
4. 指针、引用与写时拷贝
   [对象通过指针与引用间接修改对象值,并不会引起写时拷贝,很难控制;最常用的办法是写文档注释,加以忽略]
   [我觉得这一小部分意义不大,即使加了shareable变量,也只是在第一次对象的“写操作”之后生效]    
5. 带引用计数的基类
   [大概看了下,在基类中实现,需要3个辅助类,稍显复杂,而且感觉不够实用,暂且先略过吧]
6. 实现引用计数的代价: 每个被引用的值带一个引用计数,其大部分操作都需要以某种形式检查或操作引用计数。对象的值需要更多的内存,而我们在处理它们时需要执行更多的代码。
   此外,就内部的源代码而言,带引用计数的类的复杂度比不带的版本高;没有引用计数的String 类只依赖于自己。
   而文中最终的String 类如果没有三个辅助类(StringValue、RCObject 和RCPtr)就无法使用。确实,这个更复杂的设计确保在值可共享时的更高的效率;免除了跟踪对象所有权的需要,提高了引用计数的想法和实现的可重用性。但,这四个类必须写出来、被测试、文档化、和被维护,比单个类要多做更多的工作。   
7. 引用计数在下列情况下对提高效率很有用:
   少量的值被大量的对象共享。这样的共享通常通过调用赋值操作和拷贝构造而发生;对象/值的比例越高,越是适宜使用引用计数。
   对象的值的创建和销毁代价很高昂,或它们占用大量的内存。即使这样,如果不是多个对象共享相同的值,引用计数仍然帮不了你任何东西。
   [使用profiler 或其它工具来分析发现是否创建和销毁值的行为是性能瓶颈,并能得出对象/值的比例。只有有了这些数据,才能得出是否从引用计数上得到的好处超过其缺点。
   即使上面的条件满足了,使用引用计数仍然可能是不合适的。有些数据结构(如有向图)将导致自我引用或环状结构。这样的数据结构可能导致孤立的自引用对象,它没有被别人使用,而其引用计数又绝不会降到零。因为这个无用的结构中的每个对象被同结构中的至少一个对象所引用。商用化的垃圾收集体系使用特别的技术来查找这样的结构并消除它们。

   [关于简单的引用计数实现参考“C++ string小结1”]
    
    Item M30:代理类
1. Proxy 类可以完成一些其它方法很难甚至不可能实现的行为: 1.多维数组; 2.左/右值的区分是第二个; 3.限制隐式类型转换。
   [代理类实现多维数组看懂了,后面两个看的有点晕]

   [用代理类来实现一些很难甚至不可能实现的行为,但是不容易实现,不好理解]   
   [这一小节在前一节的基础上告诉我实现一个string有多么的烧脑,需要考虑的东西比我想象中要多]
   [暂且放一放,等状态好点了再来看]

    Item M31: 让函数根据一个以上的对象来决定怎么虚拟
1. 如果你需要实现二重调度,最好的办法是修改设计以取消这个需要。如果做不到的话,虚函数的方法比RTTI 的方法安全,但它限制了你的程序的可控制性(取决于你是否有权修改头文件)。另一方面,RTTI 的方法不需要重编译,但通常会导致代码无法维护。
   [需要在每个类中实现自己与其它类的关系处理]
2. 模拟虚函数表的方法。
   [感觉本质上还是通过“函数指针”建立好自己与其它类对象处理的映射(Map)方法]

   [初看时很懵,甚至工作中没有用到过,但这确实是解决问题中一个办法]



0 0
原创粉丝点击