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

    《More Effective C++》+《Effective C++》,两本经典双剑合璧,必然威力无穷。
   
    《More Effective C++》的第5章约占全书的45%,是全书最难的部分,有些地方看的人很晕,但大多会让人心悦诚服;大多的技巧都不局限于C++,可应用于所有语言的编程思想。
    为了便于消化,我将这一章分为两部分来阅读。   
   
    Item M25:将构造函数和非成员函数虚拟化
1. "虚拟构造函数"是指能够根据输入给它的数据(参数)的不同而建立不同类型的对象。(虚拟构造函数在很多场合下都有用处,从磁盘(或者通过网络连接,或者从磁带机上)读取对象信息只是其中的一个应用。)
   [通过模板也可以实现,通过传入不同的模板类型,建立不同类型的对象,啊哈!]  
2. "虚拟拷贝构造函数"能返回一个指针,指向调用该函数的对象的新拷贝; 类的虚拟拷贝构造函数只是调用它们真正的拷贝构造函数。
   [在基类中定义一个clone()虚函数,派生类各自在clone()中调用拷贝构造函数,返回派生类型的指针]
   [注意: 虚拟函数返回值类型规则:派生类重定义的虚拟函数不用必须与基类的虚拟函数具有一样的返回类型;如果虚函数的返回类型是一个指向基类的指针(或一个引用),那么派生类的函数可以返回一个指向基类的派生类的指针(或引用)。]
3. 非成员函数虚拟化实现: 编写一个虚拟函数(类成员函数)来完成工作,然后再写一个非虚拟函数(非成员函数),它什么也不做只是调用这个(类成员)虚拟函数。

   [构造函数不能为虚,非成员函数也不能为虚;通过在构造函数或非成员函数内部创建虚函数,造成构造函数与非成员函数的虚拟化]

    Item M26 限制某个类所能产生的对象数量
1. 阻止建立某个类的对象,最容易的方法就是把该类的构造函数声明在类的private域。
2. 一个Printer只能有1个对象,限制Printer对象数量的设计由三部分组成: 
   1) Printer 类的构造函数是private,这样能阻止建立对象;
   2) 全局函数thePrinter 被声明为类的友元,让thePrinter 避免调用私有构造函数引起的限制。
   3) thePrinter 包含一个静态Printer 对象,这意味着只有一个对象被建立。
   [单例模式实际就是限制只有1个对象,而我常常实现的时候将静态对象放置在类成员函数中]
   [将静态对象放置在类成员函数中的坏处: 这个函数最适合做为内联函数使用。然而它不能被声明为内联。为什么呢?“内联”意味着编译器用函数体替代该对函数的每一个调用,带有内部链接的函数可能在程序内被复制(也就是说程序的目标(obj)代码可能包含一个以上的内部链接函数的代码),这种复制也包括函数内的静态对象。如果建立一个包含局部静态对象的非成员函数,你可能会使程序的静态对象的拷贝超过一个!!!]
   [使用对象计数,可以解决上述的问题,一旦需要太多的对象,就抛出异常]      
3. 唯一的Pritner 对象是位于"函数里的静态成员"而不是在"类中的静态成员",这样做是非常重要的。在类中的静态对象实际上总是被构造(和释放),即使不使用该对象。与此相反,只有第一次执行函数时,才会建立函数中的静态对象,所以如果没有调用函数,就不会建立对象。
   [如果是类成员函数中的静态成员呢,也只是在调用类成员函数时才会构造与析构静态成员吧!!!]
4. 建立C++一个理论支柱是你不需为你不用的东西而付出。   
5. 带有内部链接的函数可能在程序内被复制(也就是说程序的目标(object)代码可能包含一个以上的内部链接函数的代码),这种复制也包括函数内的静态对象。”结果如何?如果建立一个包含局部静态对象的非成员函数,你可能会使程序的静态对象的拷贝超过一个!所以不要建立包含局部静态数据的非成员函数。   
6. 编写一个具有实例计数功能的基类,然后让像Printer 这样的类从该基类继承。使用一种方法封装全部的计数功能,不但封装维护实例计数器的函数,而且也封装实例计数器本身。   
   [在基类中实现]
   [这一小节的内容参考下之前的总结“ 单例模式”]
      
    Item M27 要求或禁止在堆中产生对象
1. 禁止非堆对象的创建方法: 把构造函数和析构函数声明为private,但这样做副作用太大,最好让构造函数成为public,让析构函数成为private。可以引进一个专用的伪析构函数,用来访问真正的析构函数,客户端调用伪析构函数释放他们建立的对象。
   把全部的构造函数都声明为private的缺点是一个类经常有许多构造函数,类的作者必须记住把它们都声明为private,否则如果这些函数就会由编译器生成,构造函数包括拷贝构造函数,也包括缺省构造函数;编译器生成的函数总是public,因此仅仅声明析构函数为private 是很简单的,因为每个类只有一个析构函数。
2. 通过限制访问一个类的析构函数或构造函数来阻止建立非堆对象,这种方法也禁止了继承和包含(聚合、组合)关系。
   通过把基类的析构函数声明为protected(同时它的构造函数还保持public)就可以解决继承的问题,需要包含对象的类可以修改为包含指向类的指针:   
3. 对象的建立有这三种情况:对象被直接实例化;对象做为派生类的基类被实例化;对象被嵌入到其它对象内。
   禁止用户直接实例化对象很简单,利用new 操作符总是调用operator new 函数把它声明为private。   
   如果operator new 和operator delete 没有在派生类中被声明为public(进行改写 ),它们就会继承基类中private 的版本,从而对象做为派生类的基类实例化被禁止;
   如果operator new 和operator delete 在派生类中被声明为public,基类的对象将被创建。
   而被嵌入的含有private的operator new 和operator delete的类,对象会被创建。
4. 判断一个对象是否在堆中的方法:
   1. 在栈中的对象,都是从高地址向低地址分配,而堆对象是从低地址向高地址分配;通过一个“临时对象”与“对象地址”比较,如果临时对象的地址大于“对象地址”,则此对象地址在堆上。如果此对象地址在栈上,由于其先分配,必然大于“临时对象地址”。   
   2. 重载operator new,将调用operator new 的对象存储在容器中,当需要判断一个对象是否在堆中创建时,查找容器中的数据来判断。     

    Item M28 智能指针
1. 不能把auto_ptr 做为参数传递,这只意味着不能使用传值的方法;通过const 引用传递auto_ptr 可以避免传值所产生的风险。
   [因为值传递,对象发生拷贝和析构,意味着auto_ptr的所有权发生转移后再被析构,原auto_ptr就不可用了] 
2. 智能指针解引用 pointee 不用必须指向T 类型对象,它也可以指向T 的派生类对象,所以解引用方法返回类型是一个引用。   
3. 提供隐式类型转换操作符可以判断一个智能指针是否为空(operator void*();).
   隐式类型转换运算符像所有的类型转换函数一样有一个缺点:在一些情况下虽然大多数程序员希望它调用失败,但是函数确实能够成功地被调用。
   [不管如何转换,不管什么类型转换,都会调用到隐式类型转换运算符???]
4. 除非有一个让人非常信服的原因去这样做,否则绝对不要提供转换到dumb指针的隐式类型转换操作符。
5. SmartPtr<>的多态不能进行编译的原因是不能把SmartPtr<Derived>转换成SmartPtr<Base>。
   从编译器的观点来看,这2个类之间没有任何关系,因为SmartPtr<Derived> 不是从SmartPtr<MusicProduct>继承过来的,这些类之间没有继承关系,我们不可能要求编译器把一种对象转换成(完全不同的)另一种类型的对象。
6. SmartPtr<CD> 与SmartPtr<const CD>是完全不同的类型。在编译器看来,它们是毫不相关的,所以没有理由相信它们是赋值兼容的。
   到目前为止这是一个老问题了,把它们变成赋值兼容的唯一方法是你必须提供函数,用来把SmartPtr<CD> 类型的对象转换成SmartPtr<const CD>类型。   
   [SmartPtr<CD> 与SmartPtr<const CD>]
5. 灵巧指针的使用在一些领域受到极大的限制,例如测试空值、转换到dumb 指针、继承类向基类转换和对指向const 的指针的支持;同时灵巧指针的实现、理解和维护需要大量的技巧;调试使用灵巧指针的代码也比调试使用dumb 指针的代码困难。
   无论如何你也不可能设计出一种通用目的的灵巧指针,能够替代dumb 指针。

   [关于auto_ptr,参考“ C++智能指针auto_ptr ”]   

   [看了这一小节,发现自己对auto_ptr的理解是不全面的,auto_ptr虽然好用,但是还是有很多局限性]


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值