Effective C++ 读书笔记9(32~34)
2012-06-02 11:56
288 查看
6 继承与面向对象设计
条款32:确定你的public集成塑模出is-a关系
请记住:
public继承意味is-a,适用于base classes深证的每一件事情也一定适用于derived classes身上,因为每一个derived class对象也都是一个base class对象
条款33:避免遮掩继承而来的名称
Derived作用域:mf1(1个函数);mf4(1个函数);
对于mf4的实现,当编译器看到mf2,会查找各作用域,看看有没有某个名为mf2的声名式。首先查找local作用域,然后查找Derived作用域,然后查找base作用域。
看下边的例子,这次重载mf1和mf3,并添加新版mf3到Derived中去:
Derived作用域:mf1(1个函数);mf3(1个函数);mf4(1个函数);
从名称观点来看,Base::mf1和Base::mf2不再被Derived继承:
Derived d;
int x;
d.mf1();
d.mf1(x); //错误,Derived::mf1遮盖了Base::mf1
d.mf2();
d.mf3();
d.mf3(x); //错误,Derived::mf3遮掩了Base::mf3
如你所见,上述规则依然适用,即使函数参数不同。
这些行为背后的理由是为了防止你在程序库或应用框架内建立新的derived类时附带的从base 类继承重载函数,但有时你通常会想继承重载函数。实际上如果你正在使用的public继承而又不继承那些重载函数,就是违反了is-a关系,那么该怎么办呢?可以使用using达到目标:
Base作用域:mf1(2个函数);mf2(1个函数);mf3(2个函数);
Derived作用域:mf1(2个函数);mf3(2个函数);mf4(1个函数);
此时:
Derived d;
int x;
d.mf1();
d.mf1(x); //ok,调用Base::mf1
d.mf2();
d.mf3();
d.mf3(x); //ok,调用Base::mf3
请记住:
1.derived类内的名称会遮掩base类的名称。在public继承下没有人希望如此。
2.为了让被遮掩的名称重见天日,可以使用using声明式或转交函数(forwarding functions)
条款34:区分接口继承和实现继承
作为类设计者:
1.有时希望derived类只继承成员函数的接口;
2.有时希望继承函数的接口和实现,但又希望能够override所继承的实现;
3.有时希望derived class同时继承函数的接口和实现;
1.成员函数的接口总是会被继承。
Shape的三个函数类型不同,有什么样的暗示呢?
1.1 生命一个pure virtual函数的目的是为了让derived类只继承函数接口。
令人意外的是,我们竟然可以为pure virtual函数提供定义,也就是说你可以为shape::draw提供一份实现代码,C++并不会发出怨言,但调用它的唯一途径是调用时明确指出其类名:
但是,允许一般虚函数同时指定函数声明和缺省行为,却有可能造成危险:
此处的fly函数为普通虚函数,有缺省的实现。A和B类型飞机如果不重写fly函数,就会继承之。如果又来了一个新款的C飞机,其实并不想按照缺省的实现飞行,但是又忘了自己写实现,那么就会继承缺省实现,但新款飞机用缺省实现飞出来的效果可能并不好。怎么办?
好了,若想使用缺省实现(A和B),可以在其fly函数中对defaultFly做一个inline调用:
现在C不可能意外的继承不正确的fly实现代码了,因为AirPlane中的pure virtual函数迫使C必须提供自己的fly版本。
(如果把fly声明为pure的,那么所有的derived类都不能随意继承fly了,所有这里这样搞一下有意义吗?)
1.3 声明non-virtual函数的目的是为了令derived class继承函数的接口及一份强制性实现。
请记住:
1.接口继承和实现继承不同。在public继承之下,derived class总是继承base class的接口。
2.pure virtual函数只具体指定接口继承。
3.non-virtual函数具体指定接口继承及缺省实现继承。
4.non-virtual函数具体指定接口继承以及强制性实现继承。
条款32:确定你的public集成塑模出is-a关系
请记住:
public继承意味is-a,适用于base classes深证的每一件事情也一定适用于derived classes身上,因为每一个derived class对象也都是一个base class对象
条款33:避免遮掩继承而来的名称
class Base{ private: int x; public: virtual void mf1() = 0; virtual void mf2(); void mf3(); }; class Derived: public Base{ public: virtual void mf1(); void mf4(); }; void Derived::mf4(){ mf2(); }Base的作用域: mf1(1个函数);mf2(1个函数);mf3(1个函数);
Derived作用域:mf1(1个函数);mf4(1个函数);
对于mf4的实现,当编译器看到mf2,会查找各作用域,看看有没有某个名为mf2的声名式。首先查找local作用域,然后查找Derived作用域,然后查找base作用域。
看下边的例子,这次重载mf1和mf3,并添加新版mf3到Derived中去:
class Base{ private: int x; public: virtual void mf1() = 0; virtual void mf1(int); virtual void mf2(); void mf3(); void mf3(double); }; class Derived: public Base{ public: virtual void mf1(); void mf3(); void mf4(); };Base作用域:mf1(2个函数);mf2(1个函数);mf3(2个函数);
Derived作用域:mf1(1个函数);mf3(1个函数);mf4(1个函数);
从名称观点来看,Base::mf1和Base::mf2不再被Derived继承:
Derived d;
int x;
d.mf1();
d.mf1(x); //错误,Derived::mf1遮盖了Base::mf1
d.mf2();
d.mf3();
d.mf3(x); //错误,Derived::mf3遮掩了Base::mf3
如你所见,上述规则依然适用,即使函数参数不同。
这些行为背后的理由是为了防止你在程序库或应用框架内建立新的derived类时附带的从base 类继承重载函数,但有时你通常会想继承重载函数。实际上如果你正在使用的public继承而又不继承那些重载函数,就是违反了is-a关系,那么该怎么办呢?可以使用using达到目标:
class Base{ private: int x; public: virtual void mf1() = 0; virtual void mf1(int); virtual void mf2(); void mf3(); void mf3(double); }; class Derived: public Base{ public: using Base::mf1; using Base::mf3; virtual void mf1(); void mf3(); void mf4(); };
Base作用域:mf1(2个函数);mf2(1个函数);mf3(2个函数);
Derived作用域:mf1(2个函数);mf3(2个函数);mf4(1个函数);
此时:
Derived d;
int x;
d.mf1();
d.mf1(x); //ok,调用Base::mf1
d.mf2();
d.mf3();
d.mf3(x); //ok,调用Base::mf3
请记住:
1.derived类内的名称会遮掩base类的名称。在public继承下没有人希望如此。
2.为了让被遮掩的名称重见天日,可以使用using声明式或转交函数(forwarding functions)
条款34:区分接口继承和实现继承
作为类设计者:
1.有时希望derived类只继承成员函数的接口;
2.有时希望继承函数的接口和实现,但又希望能够override所继承的实现;
3.有时希望derived class同时继承函数的接口和实现;
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{};
1.成员函数的接口总是会被继承。
Shape的三个函数类型不同,有什么样的暗示呢?
1.1 生命一个pure virtual函数的目的是为了让derived类只继承函数接口。
令人意外的是,我们竟然可以为pure virtual函数提供定义,也就是说你可以为shape::draw提供一份实现代码,C++并不会发出怨言,但调用它的唯一途径是调用时明确指出其类名:
Shape* ps = new Shape; //错误,shape是抽象的 Shape* ps1 = new Rectangle; ps1->draw(); Shape* ps2 = new Ellipse; ps2->draw(); ps1->Shape::draw(); //调用Shape::draw ps2->Shape::draw(); //调用Shape::draw1.2生命一般虚函数的目的,是让derived classes继承该函数的接口和缺省实现。
但是,允许一般虚函数同时指定函数声明和缺省行为,却有可能造成危险:
class Airport{}; class Airplane{ public: virtual void fly(const Airport& destination); //... }; void Airplane::fly(const Airport& destination){ //default code, fly to destination } class ModelA: public Airplane{}; class ModelB: public Airplane{};
此处的fly函数为普通虚函数,有缺省的实现。A和B类型飞机如果不重写fly函数,就会继承之。如果又来了一个新款的C飞机,其实并不想按照缺省的实现飞行,但是又忘了自己写实现,那么就会继承缺省实现,但新款飞机用缺省实现飞出来的效果可能并不好。怎么办?
class Airplane{ public: virtual void fly(const Airport& destination) = 0; //... protected: void defaultFly(const Airport& destination); }; void Airplane::defaultFly(const Airport& destination){ //default code, fly to destination }
好了,若想使用缺省实现(A和B),可以在其fly函数中对defaultFly做一个inline调用:
class ModelA: public Airplane{ public: virtual void fly(const Airport& destination){ defaultFly(destination); } }; class ModelB: public Airplane{ public: virtual void fly(const Airport& destination){ defaultFly(destination); } };
现在C不可能意外的继承不正确的fly实现代码了,因为AirPlane中的pure virtual函数迫使C必须提供自己的fly版本。
(如果把fly声明为pure的,那么所有的derived类都不能随意继承fly了,所有这里这样搞一下有意义吗?)
1.3 声明non-virtual函数的目的是为了令derived class继承函数的接口及一份强制性实现。
请记住:
1.接口继承和实现继承不同。在public继承之下,derived class总是继承base class的接口。
2.pure virtual函数只具体指定接口继承。
3.non-virtual函数具体指定接口继承及缺省实现继承。
4.non-virtual函数具体指定接口继承以及强制性实现继承。
相关文章推荐
- 《Effective C++》读书笔记之item32:确定你的public继承塑模出is-a关系
- Effective C++ 读书笔记(29-34):类与函数之实现
- 《Effective C++》读书笔记之item34:区分接口继承与实现继承
- 读书笔记《Effective C++》条款34:区分接口继承和实现继承
- Effective C++第6章继承与面向对象设计(条款32-34)
- Effective C++ 读书笔记(13-32)
- Effective C++(条款32-34)
- Effective C++ 读书笔记(三)
- Effective C++ 34 区分接口继承和实现继承
- 《Effective c++》读书笔记
- Effective C++ 读书笔记(24)
- 读书笔记 Effective C++
- Effective C++ 条款34 区分接口继承和实现继承
- Effective C++ 读书笔记(29)
- Effective C++ 读书笔记五
- 【Effective C++】读书笔记 条款52:写了placement new 也要写placement delete
- 《Effective C++》读书笔记之item31:将文件间的编译依存关系降至最低
- 《Effective C++》读书笔记之item35:考虑virtual函数以外的其他选择
- 《Effective C++》读书笔记之item39:明智而审慎地使用private继承
- 读书笔记 effective C++ 条款13: initialization list的members 初始化次序应该和其在Class内声明的次序相同