Effective C++ 一些记录和思考 (3)

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在临界区外释放资源

内容版权声明:除非注明,否则皆为本站原创文章。

转载注明出处:https://www.heiqu.com/wpjxwp.html