strategy 策略使用函数指针替换虚函数,这样每个对象都可以更灵活的有自己的特定处理函数,和运行时可以改变函数
cpp 7 class GameCharacter; 8 class HealthCalcFunc { 9 public: 10
virtual int calc(const GameCharacter&) const { 11
return 3; 12
}
13 };
14 15 HealthCalcFunc defaultHealthFunc; 16 17 class GameCharacter { 18 public: 19
explicit GameCharacter(HealthCalcFunc* phcf = &defaultHealthFunc) : pHealthCalc(phcf) {} 20
int healthValue() const { 21
return pHealthCalc->calc(*this); 22
}
23
24 private: 25
HealthCalcFunc* pHealthCalc; 26 };
27 28 int main() { 29
GameCharacter gc; 30
gc.healthValue(); 31 }
Item 36 - 绝对不重新定义继承而来的 non-virtual 函数
继承而来的 non-virtual 函数是静态绑定,父类指针只能表现出父类对象的行为, 换而言之,这个在编译期就确定好了
Item 37 - 绝不重新定义继承而来的缺省参数值
缺省参数值都是静态绑定的,应该覆盖的是 virtual 函数,而它为动态绑定的
struct B { virtual void mf(int i = 1) { std::cout << "B::mf " << i << "\n"; } // 不改变 virtual 函数的时候,可以使用 NVI 手法,实现一个外围函数,令子类调用它 }; struct D : public B { void mf() { std::cout << "D::mf\n"; } }; int main() { D d; B *pb = &d; pb->mf(); // B::mf }Item 38 - 通过复合 (composition) 塑造或根据某物实现出 has-a
在应用域,复合意味着 has-a,在实现域复合意味着 is-implemented-in-terms-of
Item 39 - 谨慎使用 private 继承
private 继承作为一种实现技术,意味着 is-implemented-in-terms-of, base class 为实现细节
当两个class不存在 is-a 关系时,其中一个需要访问另一个的 protected 成员,或者需要 重新定义 其一或者多个vitual函数,可以考虑private继承
Item 40 - 谨慎使用多重继承
多重继承易造成歧义(ambiguity)
干脆直接禁用了
模板与泛型编程
Item 41 - 了解隐式接口和编译期多态
Item 42 - 了解 typename 的双重意义
声明 template 参数时,typename 和 class 没有区别
利用 typename 标示嵌套从属类型名称(形如 C::const_iterator),但不得在 base class lists 或者 member initialization list 以他作为 base class 标示
Item 43 - 学习处理模板化基类内的名称
模板的具现是在函数使用的时候
编译器遇到 class template 继承的类时,并不知道该类的定义是什么
所以只能通过 this 指针指向 base class 对象,具现base class
在该定义域内声明需要访问的 base class 函数
直接调用base class 中的代码
struct CompanyA { void sendClear(const std::string& msg) {} }; struct CompanyB { void sendClear(const std::string& msg) {} }; struct MsgInfo {}; template <typename Company> class MsgSend { public: void send(const MsgInfo& info) { std::string msg = "foo"; Company c; c.sendClear(msg); } }; template <typename Company> class LogMsg : public MsgSend<Company> { public: // using MsgSend<Company>::send; // slove - way 2: tell complier, send() is defined in base class void sendMsg(const MsgInfo& info) { // send(info); // base: cant pass complier // this->send(info); // slove - way 1: could pass complier // MsgSend<Company>::send(info); // slove - way 3: same as way 2 } }; int main() { MsgInfo mi; LogMsg<CompanyA> ma; ma.send(mi); }Item 44 - 将与参数无关的代码抽离 template
模板只有在使用时被具现,不同类型的会被具现出不同的代码,引起代码膨胀,有点儿类似宏的用法
或者参数使用相同的二进制表述,如指针
Item 45 - 运用成员函数模板接受所有兼容类型
模板具现化后的 base class 和 derived class 为两个独立的类了
member function template 成员函数模板可接受所有兼容类型参数,也就是泛化了构造函数
为阻止编译器默认生成copy构造函数和重载=,在已经有泛化版本的情况下也须自己定义这些函数
Item 46 - 需要类型装换时须为模板定义非成员函数
模板函数在进行实参类型推导的时候,不允许进行参数的隐式转换(隐式转换是发生在函数调用的时候),而类型推导的时候是先根据已经有的定义确定函数原型
为了转换可以使用 friend ,感觉变复杂了
Item 47 - 使用 traits classes 表现类型信息
整合重载技术,使得traits class 有可能在编译期对类型执行if...else测试
使用 traits class
实现一组重载函数,或者函数模板,作为实现部分
建立一组驱动(控制)函数或者函数模板,调用以上重载函数
Item 48 - 认识模板元编程(编写模板程序在编译期执行的过程)
一些新的知识点
pimpl, 以指针指向一个对象,内含真正数据
模板全特化、偏特化的一个博客 https://blog.csdn.net/m_buddy/article/details/72973207
copy and swap, 在副本上做修改,直到修改成功时再进行写入,在陈硕的muduo书里面讲到的,用swap在临界区外释放资源