effective C++ 条款32 to 条款40
2010-11-22 11:03
369 查看
这一章是面向对象的概念
条款32:确定你的public继承塑模出is-a关系
其实这个条款正如大师一开始举得例子一样:public inheritance 意味 is-a的关系
条款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() ;
....
}
我们看mf4中mf2的查找顺序:先在mf4的作用域中找,没找到mf2,往外走,在Derived作用域找,没找到,往外,在base class中找到了,OK,结束,如果还是没找到的话,就再namespace作用域中找,最后到global作用域找
上面一切OK,但是一旦基类有重载成员,而继承类只改写了一个,那么,基类的其他重载函数就不可见了,看下面的例子:
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();
...
}
Derived d ;
int x ;
d.mf1() ;
d.mf1(x) ; // error
d.mf2() ;
d.mf3() ;
d.mf3(double) ; // error
好,如何解决上面的问题,两种方案,一是用using ,而是用转接函数
方案1:
class Derived : public Base{
public :
using Base::mf1 ;
using Base::mf3 ;
virtual void mf1() ;
void mf3() ;
void mf4();
...
}
这样一改上面就不会错了
方案2:可能using编译器不支持,也可能你不想全部要,只要一部分
class Derived : private Base{
public :
virtual void mf1()
{
Base::mf1() ;
}
}
总结:
1、derived classes内的名称会遮掩base classes内的名称,在public 继承下,从来没有人希望如此
2、为了让被遮掩的base名称能够重见天日,可以用两种方案:使用using或者使用转交函数
条款34:区分接口继承和实现继承
核心概念:区分三种函数限制符的意义:
1、纯虚函数(pure virtual):
只继承接口,可以给纯虚函数提供定义,但调用的唯一途径是调用时明确指出其class名称
2、非纯虚函数(impure virtual):
继承该函数的接口和缺省实现(书上的那个飞机的例子有点儿小夸张了)
3、非虚函数(non-virtual):
继承接口及一份强制性实现
条款35:看不懂
条款36:绝不重新定义继承而来的non-virtual函数
一个例子解决问题:
class B{
public : void mf() ;
}
class D : public B {
}
D x ;
B * pB = &x ;
D* pD = &X ;
pB->mf() ; 调用B的mf
pD->mf() ; 调用D的mf
条款37:绝不重新定义继承而来的缺省参数值
首先,你只能继承两种函数:nont-virual和virtual,如上面一个条款说,不能重新定义non-virutal函数,因此,我们可以说:绝不重新定义继承而来的virtual函数的缺省参数值。
关键概念:多态是动态绑定的,而缺省参数是静态绑定的。所以,如果你重定义继承而来的virtual函数的缺省参数值,那么,你是用基类的缺省参数值来调用继承类的函数。
class Shape{
public :
enum ShapeColor{Red , Green , Blue} ;
virtual void draw(ShapeColor color = Red) const = 0 ;
...
}
class Rectangle : public Shape{
public :
virtual draw(ShapeColor color=Green ) const ;
}
class Circle : public Shape{
public :
virtual draw(ShapeColor color) const ;
}
我们看到问题了吧,是的,你可能会再“调用一个定义于derived class内的virtual函数”的同时,却是用base class为它所指的缺省参数。
解决方案:
1、如果要重新定义,用一样的参数,但是,那样是代码重复,我们受不了,而且改动一处,忘了改另外一处就不好了
2、看下面的设计:
class Shape{
public :
enum ShapeColor(Red , Greed , Blue) ;
void draw(ShapeColor color= Red) const // 继承类不要重写该方法。该方法提供默认实参,并且调用真正办事的private函数
{
doDraw(color) ;
}
...
private :
virtual void doDraw(ShapeColor color) const = 0 ; //真正的工作,基类类需要重写
}
class Rectangle : public Shape{
public :
...
private :
virtual void doDraw(ShapeColor color) cosnt ;
}
牢记的概念:绝不重新定义一个继承而来的缺省参数值,因为缺省参数是静态绑定的,而virtual函数----你唯一应该覆写的东西,是动态绑定的
条款38:复合表示has-a 或者 “根据某物实现”
举了两个例子:
1、人有地址啊,电话号码啊,这个叫has-a
2、可以用一个list来实现一个set,这个叫根据某物实现
其实,没什么,我觉得2就像是“适配器模式”
条款39:private继承
先讲两个概念
1、编译器不会自动将一个derived class对象转换为一个base class 对象。
2、由private base class继承而来的所有成员,再derived class中都会变成private属性,纵使他们在base中是public活着protected的
private继承表示根据某物实现。private继承意味着实现部分被继承,接口部分应略去(因为private继承完后都是private的,外界根本看不到)。如果D以private继承B,意思是D对象根据B对象实现而得,再没有其他含义了。
private继承意味is-implemented-in-terms-of(根据某物实现),这个和条款38说的复合一致。大师指出:尽量用复合,只有当protected成员和/或virtual函数牵扯进来的时候,还有就是空间方面的利害关系。下面,我们分两个例子来看:
class Timer{
public :
explicit Timer(int tickFrequency) ;
virtual void onTick() const ;
}
class Widget : private Timer{
private : // private继承的东西都是private的了,别乱写,搞死别人
virtual void onTick() ;
}
大师说了,其实在这种情况下复合还是可以代替private继承,并且,他说了两个优点,我们看看代码吧,优点小夸张,呵呵
class Widget{
private :
class WidgetTimer : public Timer{
public:
virtual void onTick() const ;
};
WdigetTimer timer ;
};
空间方面的就是说继承比复合省空间,有一个EBO(empty base optimization 空白基类最优化)
我的总结:能不用private继承就不用private继承,我在实际的工作中还想从来没有用过,哈哈
条款40:多重继承
考虑下面的情况:
class A {
}
class B : public A {
}
class C : public A {
}
class D : public B , public D{
}
第一个问题:
加入B,C中都有一个mf()函数,那么
D d ;
d.mf() ; //调用的是哪个?歧义
应该这么用:d.B::mf() ; // B的mf
第二个问题:
A中的一份数据data,B也有了,C也有了,那么D中有几份?
缺省做法是2份,但可以通过virtual继承防止上面的事情发生
class B : virtual public A {
}
class C : virtual public A {
}
那么是不是所有的继承都应该用virtual public呢?
不是,如果你用virtual public , 你必然要是靠编译器在背后默默支撑你,因此,代码的体积将增大,速度将变慢。并且,virtual base class的初始化规则也复杂的多。
大师给了两个观点:
1、非必要不要用virtual base
2、如果非要用,尽量不要在里面放置数据,这样不用担心初始化的事情。(和JAVA 的 interface差不多)
条款32:确定你的public继承塑模出is-a关系
其实这个条款正如大师一开始举得例子一样:public inheritance 意味 is-a的关系
条款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() ;
....
}
我们看mf4中mf2的查找顺序:先在mf4的作用域中找,没找到mf2,往外走,在Derived作用域找,没找到,往外,在base class中找到了,OK,结束,如果还是没找到的话,就再namespace作用域中找,最后到global作用域找
上面一切OK,但是一旦基类有重载成员,而继承类只改写了一个,那么,基类的其他重载函数就不可见了,看下面的例子:
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();
...
}
Derived d ;
int x ;
d.mf1() ;
d.mf1(x) ; // error
d.mf2() ;
d.mf3() ;
d.mf3(double) ; // error
好,如何解决上面的问题,两种方案,一是用using ,而是用转接函数
方案1:
class Derived : public Base{
public :
using Base::mf1 ;
using Base::mf3 ;
virtual void mf1() ;
void mf3() ;
void mf4();
...
}
这样一改上面就不会错了
方案2:可能using编译器不支持,也可能你不想全部要,只要一部分
class Derived : private Base{
public :
virtual void mf1()
{
Base::mf1() ;
}
}
总结:
1、derived classes内的名称会遮掩base classes内的名称,在public 继承下,从来没有人希望如此
2、为了让被遮掩的base名称能够重见天日,可以用两种方案:使用using或者使用转交函数
条款34:区分接口继承和实现继承
核心概念:区分三种函数限制符的意义:
1、纯虚函数(pure virtual):
只继承接口,可以给纯虚函数提供定义,但调用的唯一途径是调用时明确指出其class名称
2、非纯虚函数(impure virtual):
继承该函数的接口和缺省实现(书上的那个飞机的例子有点儿小夸张了)
3、非虚函数(non-virtual):
继承接口及一份强制性实现
条款35:看不懂
条款36:绝不重新定义继承而来的non-virtual函数
一个例子解决问题:
class B{
public : void mf() ;
}
class D : public B {
}
D x ;
B * pB = &x ;
D* pD = &X ;
pB->mf() ; 调用B的mf
pD->mf() ; 调用D的mf
条款37:绝不重新定义继承而来的缺省参数值
首先,你只能继承两种函数:nont-virual和virtual,如上面一个条款说,不能重新定义non-virutal函数,因此,我们可以说:绝不重新定义继承而来的virtual函数的缺省参数值。
关键概念:多态是动态绑定的,而缺省参数是静态绑定的。所以,如果你重定义继承而来的virtual函数的缺省参数值,那么,你是用基类的缺省参数值来调用继承类的函数。
class Shape{
public :
enum ShapeColor{Red , Green , Blue} ;
virtual void draw(ShapeColor color = Red) const = 0 ;
...
}
class Rectangle : public Shape{
public :
virtual draw(ShapeColor color=Green ) const ;
}
class Circle : public Shape{
public :
virtual draw(ShapeColor color) const ;
}
我们看到问题了吧,是的,你可能会再“调用一个定义于derived class内的virtual函数”的同时,却是用base class为它所指的缺省参数。
解决方案:
1、如果要重新定义,用一样的参数,但是,那样是代码重复,我们受不了,而且改动一处,忘了改另外一处就不好了
2、看下面的设计:
class Shape{
public :
enum ShapeColor(Red , Greed , Blue) ;
void draw(ShapeColor color= Red) const // 继承类不要重写该方法。该方法提供默认实参,并且调用真正办事的private函数
{
doDraw(color) ;
}
...
private :
virtual void doDraw(ShapeColor color) const = 0 ; //真正的工作,基类类需要重写
}
class Rectangle : public Shape{
public :
...
private :
virtual void doDraw(ShapeColor color) cosnt ;
}
牢记的概念:绝不重新定义一个继承而来的缺省参数值,因为缺省参数是静态绑定的,而virtual函数----你唯一应该覆写的东西,是动态绑定的
条款38:复合表示has-a 或者 “根据某物实现”
举了两个例子:
1、人有地址啊,电话号码啊,这个叫has-a
2、可以用一个list来实现一个set,这个叫根据某物实现
其实,没什么,我觉得2就像是“适配器模式”
条款39:private继承
先讲两个概念
1、编译器不会自动将一个derived class对象转换为一个base class 对象。
2、由private base class继承而来的所有成员,再derived class中都会变成private属性,纵使他们在base中是public活着protected的
private继承表示根据某物实现。private继承意味着实现部分被继承,接口部分应略去(因为private继承完后都是private的,外界根本看不到)。如果D以private继承B,意思是D对象根据B对象实现而得,再没有其他含义了。
private继承意味is-implemented-in-terms-of(根据某物实现),这个和条款38说的复合一致。大师指出:尽量用复合,只有当protected成员和/或virtual函数牵扯进来的时候,还有就是空间方面的利害关系。下面,我们分两个例子来看:
class Timer{
public :
explicit Timer(int tickFrequency) ;
virtual void onTick() const ;
}
class Widget : private Timer{
private : // private继承的东西都是private的了,别乱写,搞死别人
virtual void onTick() ;
}
大师说了,其实在这种情况下复合还是可以代替private继承,并且,他说了两个优点,我们看看代码吧,优点小夸张,呵呵
class Widget{
private :
class WidgetTimer : public Timer{
public:
virtual void onTick() const ;
};
WdigetTimer timer ;
};
空间方面的就是说继承比复合省空间,有一个EBO(empty base optimization 空白基类最优化)
我的总结:能不用private继承就不用private继承,我在实际的工作中还想从来没有用过,哈哈
条款40:多重继承
考虑下面的情况:
class A {
}
class B : public A {
}
class C : public A {
}
class D : public B , public D{
}
第一个问题:
加入B,C中都有一个mf()函数,那么
D d ;
d.mf() ; //调用的是哪个?歧义
应该这么用:d.B::mf() ; // B的mf
第二个问题:
A中的一份数据data,B也有了,C也有了,那么D中有几份?
缺省做法是2份,但可以通过virtual继承防止上面的事情发生
class B : virtual public A {
}
class C : virtual public A {
}
那么是不是所有的继承都应该用virtual public呢?
不是,如果你用virtual public , 你必然要是靠编译器在背后默默支撑你,因此,代码的体积将增大,速度将变慢。并且,virtual base class的初始化规则也复杂的多。
大师给了两个观点:
1、非必要不要用virtual base
2、如果非要用,尽量不要在里面放置数据,这样不用担心初始化的事情。(和JAVA 的 interface差不多)
相关文章推荐
- 《Effective C++ 3》06 继承与面对对象设计 条款:32-40
- Effective C++(条款39-40)
- Effective C++ -----条款10: 令operator=返回一个reference to *this
- 《Effective C++》学习笔记——条款32
- 读书笔记《Effective c++》 条款10 令operator= 返回一个reference to *this
- Effective C++ 条款九、十 绝不在构造和析构过程中调用virtual函数|令operator=返回一个reference to *this
- effective C++ 条款05 to 条款12
- effective C++ 条款18 to 条款24
- effective C++ 条款 40:明智而审慎地使用多重继承
- effective C++ 条款26 to 条款31
- Effective C++:条款10:令operator=返回一个reference to *this。
- Effective C++(条款32-34)
- effective c++ 条款23 perfer nonmember nonfreind function to member function
- 《Effective C++ 》条款10:令operator=返回一个reference to *this
- Effective c++ 条款32-37 重载,重写, 重定义
- Effective C++:条款32:确定你的public继承塑模出is-a关系
- 【effective c++】条款10:令operator=返回一个reference to *this
- 读书笔记《Effective C++》条款10:令operator=返回一个reference to *this
- effective c++ 条款23 perfer nonmember nonfreind function to member function
- effective c++ 条款18 make interface easy to use correctly and hard to use incorrectly