Effective C++ 笔记(三)

第六章:继承与面向对象设计

 

  • 条款32:确定你的public继承塑模出is-a关系

  • 条款33:避免遮掩继承而来的名字

例如:

  • 条款34:区分接口继承和实现继承

接口继承和实现继承不同。在public继承之下,derived class 总是继承base class 的接口

声明一个pure virtual 函数的目的是为了让derived classes只继承函数接口

声明 impure virtual 函数的目的是让derived classes 继承该函数的接口和缺省实现

声明 non-virtual 函数的目的是为了令derived classes 继承函数接口并强制性实现。代表的是不变性。

  • 条款35:考虑virtual 函数以外的其他选择

NVI 手法,Template Method设计模式:通过 public non-virtual 成员函数间接调用 private virtual函数。把non-virtual函数称为virtual函数的外覆器。

借由函数指针实现Strategy模式。运用函数指针替换virtual 函数

  • 条款36:绝不重新定义继承而来的non-virtual 函数

  • 条款37:绝不重新定义继承而来的缺省参数值

绝对不要重新定义一个继承而来的缺省参数值,因为缺省参数值都是静态绑定的,而virtual函数-你唯一应该重写的东西--却是动态绑定。

知识点:

静态类型,就是它在程序中被声明时所采用的类型。发生在编译期

不论它们真正指向什么,它们的静态类型都是Shape*

动态类型,就是目前所指对象的类型。发生在运行期。以上例而言,pc 的动态类型是Circle*,pr 的动态类型是Rectangle*。

动态类型,可在程序执行过程中改变(通常经由赋值动作)。

Virtual 函数是动态绑定的,意思是调用一个virtual 函数时,究竟调用哪一份函数实现代码,取决于发出调用的对象的动态类型。

  • 条款38:通过复合塑模出has-a 或“根据某物实现出”

  • 条款39:明智而审慎地使用private继承

private 继承意味着只有实现部分被继承,接口部分被略去。也意味着“根据某物实现出”。所以尽可能使用复合,必要时使用private继承

  • 条款40:明智而审慎地使用多重继承(MI)


第七章  模板与泛型编程

知识点

  • 运行期多态通过虚函数发生在运行期
  • 编译器多态:以不同的模板参数具现化导致调用不同的函数。(泛型编程)
  • 显式接口:由函数的签名式(函数名称、返回类型,参数类型)组成。例如

  • 隐式接口:由有效表达式组成

  • 条款41:了解隐式接口和编译器多态

class 和 template 都支持 接口和多态。

class 而言,接口显式的,以函数签名为中心。多态则是通过virtual 函数发生于运行期

template 参数而言,接口是隐式的,多态通过template 具现化和函数重载解析发生于编译期

  • 条款42:了解typename 的双重意义

任何时候,当你想要在template 中 refer to一个嵌套从属类型名称,就必须在它前面加上关键字 typename。typename 只被用来验明嵌套从属类型名称。

声明 template 参数时,前缀关键字 class 和 typename 可互换。如 template < typename C> 或者 template<class C>

  • 条款43:学习处理模板化基类内的名称 

可在derived class template 内通过 "this->" 指涉 base class template 内的成员名称

  • 条款44:将与参数无关的代码抽离template

因非类型模板参数而造成的代码膨胀,往往可消除,做法是以函数参数或class 成员变量替换 template参数

  • 条款45:运用成员函数模板接受所有兼容类型

可以根据 SmartPtr<U>生成一个SmartPtr<T>,类型的兼容。

shared_ptr 的部分定义

  • 条款46:需要类型转换时请为模板定义非成员函数

当我们编写一个class template,而它所提供的 “与此template 相关的” 函数支持“所有参数的隐式类型转换”时,请将那些函数定义为“class template 内部的 friend 函数”。

如:

  • 当定义 Rational<int> oneHalf (1,2);  Rational<int> result=oneHalf * 2;  //无法通过编译。编译器无法推算出 2 的数据类型。因为template 实参推导过程中从不将隐式类型转换函数纳入考虑。

我们需要这样修改:

  • 因为当对象oneHalf 被声明为一个 Rational<int>,class Rational<int> 于是被具现化,而作为过程的一部分,friend 函数 operator*(接受Rational<int> 参数)也就被自动声明出来,后者身为一个函数而非函数模板,因此编译器可在调用它时使用隐式转换函数。
  • 条款47:请使用 traits classes 表现类型信息

  • 条款48:认识 template 元编程

Template metaprogramming (TMP) 模板元编程 是一个图灵机。

TMP 可将工作由运行期移往编译期,因而得以实现早期错误侦测和更好的执行效率。

 

第八章 定制new 和delete

  • 条款49:了解new-handler 的行为

operator new 抛出异常以反映一个未获满足的内存需求之前,它会先调用一个客户指定的错误处理函数,一个所谓的new-handler,调用set_new_handler可以指定该函数。

设计一个良好的new-handler 函数必须做以下事情:

  1. 让更多内存可被使用。
  2. 安装另一个new-handler (只要调用set_new_handler),让new-handler修改自己的行为
  3. 卸除new-handler,也就是将null 指针传给set_new_handler,一旦没有安装任何new-handler行为,operator new会在内存分配失败时抛出异常。
  4. 抛出 bad_alloc 的异常
  5. 不返回,通常调用abort 或 exit.

set_new_handler 允许客户指定一个函数,在内存分配无法获得满足时被调用。

  • 条款51:编写new和delete时需固守常规

operator new,如果有能力供应客户申请的内存,就返回一个指针指向那块内存,否则抛出bad_alloc异常。operator new 实际上不止一次尝试分配内存,并在每次失败后调用new-handling 函数,这里假设new-handling 函数也许能够做某些动作将某些内存释放出来。只有当指向new-handling 函数的指针是null,operator new 才会抛出异常。

C++规定,即使客户要求0 bytes,operator new 也得返回一个合法指针。

non-member operator new 伪码
void* operator new (std::size_t size) throw (std::bad_alloc)
{
   if(size==0) size=1;   //处理0-byte申请,将它视为 1-byte申请
    
   while(true){
   //尝试分配 size bytes;
   if (分配成功)
      return (一个指针,指向分配得来的内存);
  
 //分配失败
   new_handler globalHandler =set_new_handler(0);
   set_new_handler(globalHandler);
   if(globalHandler) (*globalHandler)();
   else  throw std::bad_alloc();
  }
}

operator new内含一个无穷循环,退出循环的唯一办法是:内存被成功分配或 new-handling函数做了一件条款49提到的事情,或是承认失败直接return。

  • 条款52:写了placement new 也要写placement delete

假设你写了一个class 专属的operator new,要求接受一个ostream 用来log相关分配信息。

如果operator new 接受的参数除了size 之外还有额外参数,这个就是所谓的placement new。意味着带任意额外参数的new

典型的placement new 版本如下:

void* operator new (std::size_t, void* pMemory) throw(); //placement new

这个版本的new 已被纳入C++标准程序库,只要#include<new>就行。这个版本的new 用途之一是负责在vector 的未使用空间上创建对象。一个特定位置上的new。

当你写一个placement operator new,请确定也写出了对应的placement operator delete。如果没有这样做,你的程序可能会发生内存泄漏。

当构造函数抛出异常时,对应的placement delete 会被自动调用。如果没有抛出异常,当客户delete pw 时,调用的是正常版本的operator delete。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值