Effective C++笔记(下)

第6章 继承与面向对象设计

这章讨论的是C++中如何面向对象编程(OOP),从继承、派生、virtual函数方面介绍C++中的OOP。

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

“public 继承”表示is-a的关系。适用于base classes身上的每一件事情一定也适用于derived classes身上,因为每一个derived class 对象也都是一个base class对象。
is-a (属于)、has-a(包含) 、is-implemented-in-terms-of (根据某物实现出) 是classes之间常见的三种关系。

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

  1. derived classes内的名称会遮掩base classes内的名称。在public继承下从来没有人希望如此。即,派生类中成员会覆盖基类中的同名成员。
  2. 可以使用using 声明式或转交函数来避免名称遮掩。using 声明式:在派生类中暴露被遮掩的指定名称的所有成员;转交函数:在派生类中的函数里调用某个同名的特定成员函数。

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

  1. 接口继承和实现继承不同,在public继承下,derived class总是继承base class的接口。(private继承呢?)
  2. pure virtual函数只具体指定接口继承。(derived class 必须补充函数实现)
  3. impure virtual函数具体指定接口继承及缺省实现继承。
  4. non-virtual函数具体指定接口继承以及强制性实现继承。(非虚函数可以被派生类重写,但是不符合oop的原则)
  5. 不要随意将所有函数声明为non-virtual,也不要随意将所有函数声明为virtual。

条款35:考虑virtual函数以外的其他选择( 难理解,先简单摘要一下

  1. 使用non-virtual interface(NVI)手法,以public non-virtual成员函数包裹较低访问性(private或protected)的virtual函数。
  2. 使用“函数指针成员变量”替换virtual函数。
  3. 以tr1::function成员变量替换virtual函数。
  4. 将继承体系类的virtual函数替换为另一个继承体系内的virtual函数。

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

public继承表示“is-a”的关系,non-virtual函数说明该接口和实现都应该被继承,并且对该函数的重新定义是错误的行为。

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

绝对不要重新定义一个继承而来的缺省参数值,因为缺省参数值都是静态绑定,而virtual函数——你唯一应该覆写的东西——却是动态绑定。
补充:对象的静态类型是指它在程序中被声明时所采用的的类型;动态类型则是指“目前所指对象的类型”。静态绑定就是调用对象的静态类型内的函数,而动态绑定则是调用动态类型的函数。

class Base
{
    
    
...
};
class Derived:public Base
{
    
    
...
}
Base *pb; //pb的静态类型为Base,无动态类型
Base *pd = new Derived;	//pd的静态类型为Base,动态类型为Derived

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

  1. 复合是类型之间的一种关系,表示某种类型的对象内含其它类型的对象。也叫分层、内含、聚合或内嵌。
  2. 在应用域,复合意味着has-a(有一个)。在实现域,复合意味着is-implemented-in-terms-of(根据某物实现出)

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

  1. Private 继承意味is-implemented-in-terms of(根据某物实现出)。它通常比复合(composition)的级别低。但是当derived class需要访问protected base class 的成员,或需要重新定义继承而来的virtual函数,这么设计是合理的。
  2. 与复合不同,private继承可以造成empty base空间最优化。这对致力于“对象尺寸最小化”的程序库开发者而言,可能很重要。

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

  1. 多重继承比单一继承复杂。它可能导致新的歧义性,以及对virtual继承的需要。
  2. virtual继承会增加大小、速度、初始化(及赋值)复杂度等成本(因为virtual继承会使derived class的对象内含一个virtual base table pointer)。如果virtual base classes不带任何数据,将是最具实用价值的情况。
  3. 多重继承有正当用途。其中一个情节涉及“public继承某个interface class”和“private 继承某个协助实现的class”的两项组合。

第7章 模板与泛型编程

本章关注的焦点是如何较好的基于template编程。

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

  1. classes 和 templates 都支持接口(interfaces)和多态(polymorphism).
  2. 对classes 而言接口是显式的(explicit),以函数签名为中心。多态则是通过virtual函数发生于运行期。
  3. 对template参数而言,接口(模板内所有可能用上的函数)是隐式的(implicit),奠基于有效表达式。多态则是通过template具现化和函数重载解析发生于编译期。

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

  1. 声明template参数时,前缀关键字class和typename可互换。
  2. 请使用关键字typename标识嵌套从属类型名称;但不得在base class lists(基类列)或member initialization list(成员初值列)内以它作为base class 修饰符。

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

当以类模板(base class templates)为基类进行派生时,derived class templates不能像面向对象编程时一样直接访问base class templates的名称。因为derived class templates定义式在编译时无法确定所继承的base class 究竟是什么样的class,甚至都不能确定base class是否含有这个被访问的名称,所以编译器拒绝进入base class template寻找继承而来的名称。文中有三种方法来解决这个问题:

  1. 在base class 函数调用动作之前加上“this->”
  2. 使用using 声明式,告诉编译器,所使用名称的的base class
  3. 在该名称前明确写出“base class 资格修饰符(但是会关闭virtual绑定行为)

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

  1. Templates生成多个classes和多个函数,所以任何template代码都不该与某个造成膨胀的template参数产生相依关系(将template具现后会产生相同代码的部分抽离出来)。
  2. 因非类型模板参数(表示类型以外的参数)而造成的代码膨胀,往往可消除,做法是以函数参数或class成员变量替换template参数。
  3. 因类型参数(表示类型的参数)而造成的代码膨胀,往往可降低,做法是让带有完全相同二进制表述的具现类型共享实现码。

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

  1. 类型兼容的含义就是支持类型转换,为了使模板类支持类型兼容,需要在模板类里使用成员函数模板,这样模板类具现化时便可生成“可接受所有兼容类型”的函数。
  2. 如果我们声明成员模板用于“泛化copy构造函数”或“泛化copy assignment操作”,那么我们还是需要声明正常的copy构造函数和copy assignment操作符,否则编译器将会替我们完成这个任务。

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

当我们编写一个class template,而它所提供的“与此template相关的”函数需要支持“所有参数都可隐式转换”时,请将这些函数定义为“class template内部的friend函数”。
这个问题的关键点在于“class template内的成员函数不支持将实参隐式转换成由此template具现的class类型”。(我也没有完全看懂这个问题的解释,先跳过吧)

条款47:请使用traits classes表现类型信息

  1. Traits classes(萃取器)使得“类型相关信息”在编译期可用。它们以templates和“templates特化”完成实现。
  2. 整合重载技术后,traits classes有可能在编译期对类型执行if…else测试。
  3. 如何设计并实现一个traits class:首先确认我们希望将来可取得的类型相关信息;为该信息选择一个名称;提供一个template和一组特化版本,内含你希望支持的类型相关信息。
  4. 如何使用一个traits class:首先建立一组重载函数(劳工函数)或函数模板,彼此间的差异只在于各自的traits参数,令每个函数实现码与其接受之traits信息相应和;然后建立一个控制函数(工头函数)或函数模板,它调用上述那些“劳工函数”并传递traits class所提供的信息。

条款48:认识template元编程(适合中高级程序员

  1. Template metaprogramming(模板元编程)可将工作由运行期移往编译期,因而得以实现早期错误侦测和更高的执行效率。
  2. TMP可被用来生成“基于政策选择组合”的客户定制代码,也可用来避免生成对某些特殊类型并不适合的代码。

第8章 定制new和delete

大概了解一下吧

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

new-handler是操作符new的内存需求不能被满足时由客户指定被调用的错误处理函数,new-handler会循环被调用直到动态内存被成功分配,new-handler可以由标准库函数set_new_handler设置。

条款50:new和delete的合理替换时机

  1. 为了检测运用错误;
  2. 为了收集动态分配内存之使用统计信息;
  3. 为了增加分配和归还的速度;
  4. 为了降低缺省内存管理器带来的空间额外开销。
  5. 为了弥补缺省分配器中的非最佳齐位。
  6. 为了将相关对象成簇集中。
  7. 为了获得非传统的行为。

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

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

猜你喜欢

转载自blog.csdn.net/gaggagaasd/article/details/108906201