Effective C++ - Inheritance and Object-Oriented Design
2017-10-31 22:45
399 查看
Effective C++ - Inheritance and Object-Oriented Design
前言:OOP面向对象编程(继承,单一继承,多重继承,public/protected/private,virtual/non-virtual,继承的查找规则,virtual函数是最佳选择吗,等等)有哪些坑?Effective C - Inheritance and Object-Oriented Design
确定你的public继承是is-a关系
避免遮掩继承而来的名称
区分接口继承和实现继承
考虑virtual函数以外的其他选择
1 Non-Virtual Interface NVI
2 Function Pointers 实现 Strategy 模式
3 tr1function 实现 Strategy 模式
1 确定你的public继承是is-a关系
Make sure public inheritance models “is-a”.例子:
class Person { // ... }; class Student: public Person { // ... };
每个学生都是人,但并非每个人都是学生。人的概念比学生更一般化,学生是人的一种特殊形式。
这个论点,只对
public继承才成立。只有当Student以public形式继承Person,C++的行为才如上述描述。
private继承的意义与此完全不同,至于
protected继承,其意义更加困惑。
请记住
public继承意味
is-a。适用于
base classes身上的每一件事情一定适用于
derived classes身上,因为,每一个
derived class对象也都是一个
base class对象。
2 避免遮掩继承而来的名称
#include <iostream> class Base { public: virtual void f1() = 0; virtual void f1(int) { std::cout << "virtual void Base::f1(int)\n"; } virtual void f2() { std::cout << "virtual void Base::f2()\n"; } void f3(); }; class Derived: public Base { public: // 让Base class内名为f1的函数在Derived class作用域内可见,如果不这样声明,下面d.f1(1)会找不到 using Base::f1; virtual void f1() { std::cout << "virtual void Derived::f1()\n"; } void f3() { std::cout << "void Derived::f3()\n"; } }; int main() { Derived d; d.f1(); d.f1(1); // error ? d.f2(); d.f3(); return 0; } /* virtual void Derived::f1() virtual void Base::f1(int) virtual void Base::f2() void Derived::f3() */
请记住
* derived classes内的名称会遮掩base classes内的名称。在public继承下,正常是不希望被遮掩的。
* 为了让遮掩的名称可见,可以使用
using声明来到达目的(如上述例子)。
3 区分接口继承和实现继承
Differentiate between inheritance of interface and inheritance of implementation.表面上直接了当的public继承概念,经过更严密的检查之后,发现它由两部分组成:
函数接口(function interfaces)继承
函数实现(function implementations)继承
class Shape { public: // 三种被继承的接口 virtual void draw() const = 0; virtual void error(const std::string& msg); int objectID() const; // ... }; class Rectangle: public Shape { // ... }; class Ellipse: public Shape { // ... };
Shape是一个抽象class,它的pure virtual函数draw使它成为一个抽象class。所以客户不能够创建Shape class的实体,只能创建其derived classes的实体。
三种以public继承的接口,含义是不一样的:
成员函数接口总是会被继承。
声明一个
pure virtual函数的目的是,为了让derived classes只继承函数接口。
声明简朴的
impure virtual函数的目的,是让derived classes继承该函数的接口和缺省实现。
声明
non-virtual函数的目的,是为了令derived classes继承函数的接口,和一份强制性实现。
4 考虑virtual函数以外的其他选择
Consider alternatives to virtual functions.class GameCharacter { public: virtual int healthValue() const; // 返回游戏中人物的健康指数,derived classes可以重新定义此函数 // ... };
healthValue并未被声明为
pure virtual,这暗示我们将会有个计算健康指数的缺省算法。
一些替代方案:
4. 1 Non-Virtual Interface (NVI)
就是,令客户通过public non-virtual成员函数间接调用private virtual函数。这样做的好处是,可以在public non-virtual函数(也就是virutal函数的wrapper函数)中完成一些事前和事后的工作。class GameCharacter { public: int healthValue() const { // 返回游戏中人物的健康指数,derived classes不重新定义此函数 do_something_before(); int ret = doHealthValue(); // 做真正的工作 do_something_after(); } private: virtual int doHealthValue() const { // derived classes可以重新定义它 // 缺省实现 } };
4.2 Function Pointers 实现 Strategy 模式
这种方法的思路是,人物健康指数的计算与人物类型无关。这样的计算完全不需要人物这个成分。例如,我们可能会要求每个人物的构造函数接受一个指针,指向一个健康计算函数,而我们可以调用该函数进行实际计算。class GameCharacter; // 前置声明 forward declaration int defaultHealthCalc(const GameCharacter& gc); class GameCharacter { public: typedef int (*HealthCalcFunc) (const GameCharacter&); explicit GameCharacter(HealthCalcFunc hcf = defaultHealthCalc) : healthFunc(hcf) { // init } int healthValue() const { return healthFunc(*this); } // ... private: HealthCalcFunc healthFunc; // 函数指针 };
这种实现更加具有弹性:
同一个人物类型的不同实例,可以有不同的健康计算函数。
某已知人物的健康指数计算函数,可以在运行时期变更。
事物都有两面性:
一般而言,唯一能够解决,需要以
non-member函数访问class的
non-public成分的办法就是:弱化class的封装。例如,class可声明那个
non-member函数为
friends,或是为其实现某一部分提供public访问函数。
运用函数指针替换virtual函数。其优点是,每个对象可各自拥有自己的健康计算函数和可在运行时期改变计算函数;而缺点,是可能必须降低类的封装性。
4.3 tr1::function 实现 Strategy 模式
一旦习惯了templates,以及它们对隐式接口的使用,基于
函数指针的做法看起来便过分苛刻而死板了。
为什么要求“健康指数的计算”必须是函数,而不能是某种”像函数的东西”,例如,函数对象。如果我们不再使用函数指针,而是改用一个类型为
tr1::function的对象,这些约束就全部挥发不见了。这样的对象,可以持有任何可调用物(也就是,函数指针、函数对象、成员函数指针)。
TODO
相关文章推荐
- effective C++ 学习(Inheritance and Object-Oriented Design)
- Part6 继承与OOD Inheritance and Object-Oriented Design(三)
- Part6 继承与OOD Inheritance and Object-Oriented Design(二)
- (Effective C++)第六章 继承与面向对象(Inheritance and Object-Oriented Design)
- Multi-Language Programming : Entity Object Inheritance And Function Overloading_3
- Navigating C++ and Object-Oriented Design
- 函数继承Effective C++ 读书笔记之Part6.Inheritance and Object-Oriented Design
- Object-orientation and inheritance in JavaScript: a comprehensive explanation
- Effective C++ 读书笔记之Part6.Inheritance and Object-Oriented Design
- Conclusion for Inheritance and Object Oriented Design
- [翻译] Effective C++, 3rd Edition, Chapter 6. Inheritance(继承)和 Object-Oriented Design(面向对象设计)
- Effective C++(Item3) Prefer new and delete to malloc and free
- Head First Object-Oriented Design and Analysis 学习笔记(八)
- Exemplar-SVMs for Object Detection and Beyond--Details (二)
- Avoid object allocations during draw/layout operations (preallocate and reuse instead)
- [深度学习论文笔记][Object Detection] Rich feature hierarchies for accurate object detection and semantic seg
- Object-C系列-<present and dismiss>
- C# DynamicObject And ExpandoObject
- Entity Framework 中 使用Include 解决ObjectContext instance has been disposed and can no longer be used for operations that require a connection错误 -原创
- Object-Oriented Analysis and Design Using UML 翻译与学习 (十)