当前位置: 58彩票app下载 > 计算机网络 > 正文

读书笔记,永远不要重新定义继承而来的函数默

时间:2019-09-24 00:53来源:计算机网络
  从一开首就让大家简化此番的座谈。你有两类你能够继续的函数:虚函数和非虚函数。不过,重新定义一个非虚函数总是错误的(Item36),所以大家得以安枕而卧的把那几个条款的座

 

从一开首就让大家简化此番的座谈。你有两类你能够继续的函数:虚函数和非虚函数。不过,重新定义一个非虚函数总是错误的(Item 36),所以大家得以安枕而卧的把那几个条款的座谈限定在一而再带私下认可参数值的虚函数上。

读书笔记 effective c++ Item 37 永久不要再一次定义承继而来的函数默许参数值,effectiveitem

1. 虚函数是动态绑定的,而私下认可参数是静态绑定的

在这种气象下,这些条目的认证就一定直白了:虚函数是动态绑定的,而暗中认可参数值是静态绑定的。

那是何许?你说您不堪重负的底部已经忘记了动态绑定和静态绑定之间的差距?(为了好记,静态绑定也称为早绑定(early binding),动态绑定也叫做晚绑定(late binding))。让我们看一下:

贰个目的的静态类型是您早已在程序文件中声称的项目,思虑如下的类承继种类:

 1 // a class for geometric shapes
 2 class Shape {
 3 public:
 4 enum ShapeColor { Red, Green, Blue };
 5 // all shapes must offer a function to draw themselves
 6 virtual void draw(ShapeColor color = Red) const = 0;
 7 ...
 8 };
 9 
10 class Rectangle: public Shape {
11 public:
12 // notice the different default parameter value — bad!
13 virtual void draw(ShapeColor color = Green) const;
14 ...
15 };
16 class Circle: public Shape {
17 public:
18 virtual void draw(ShapeColor color) const;
19 ...
20 };

 

画成类承接图会是上面这几个样子:

 图片 1

当今虚拟四个指针:

1 Shape *ps;                               // static type = Shape*
2 
3 Shape *pc = new Circle;           // static type = Shape*
4 
5 Shape *pr = new Rectangle;    // static type = Shape*

 

在那几个事例中,ps,pc和pr都被声称为指向shape的指针,所以它们用Shape作为它们的静态类型。注意无论shape指针真正指向的是如何指标,静态类型都以Shape*。

 

叁个对象的动态类型由指针当前本着的对象类型来支配。也正是,它的动态类型表明了它的一坐一起会是哪些的。看下边包车型大巴例证,pc的动态类型是Circle*,pr的动态类型是Rectangle*。对于ps,它事实上并未有动态类型,因为它还一贯不援用任何对象。

 

正如字面意思所代表的,在程序运行时动态类型是能够变动的,非常是经过赋值:

1 ps = pc;      // ps’s dynamic type is now Circle* 
2 ps = pr;      // ps’s dynamic type is now Rectangle*

虚函数是动态绑定的,意味着哪个函数被调用是由爆发调用的对象的动态类型来支配的:

1 pc->draw(Shape::Red);          // calls Circle::draw(Shape::Red)
2 
3 pr->draw(Shape::Red);          // calls Rectangle::draw(Shape::Red)

 

那么些都以旧文化了,小编明白您早晚领悟虚函数。当您着想带默许参数值的虚函数时,麻烦出现了,因为虚函数是动态绑定的,可是暗中认可参数是静态绑定的。这表示你可能会停下五个虚函数的调用,因为函数定义在派生类中却接纳了基类中的私下认可参数:

 

1  pr->draw();                           // calls Rectangle::draw(Shape::Red)!

 

在这种景观中,pr的动态类型是Rectangle*,所以Rectangle的虚函数被调用,那也是您所愿意的。在Rectangle::draw中,默认参数值是Green。可是因为pr的静态类型是Shape*,这几个函数调用的默许参数值是来自于Shape类实际不是Rectangle类!最终的结果是其一调用由多少个意想不到的也差没有多少是您意料不到的整合组成:也便是Shape类和Rectangle类中的draw注解混合而成。

 

Ps,pc和pr都为指针不是造成这些主题材料的原因。假使它们是援用也一直以来会油但是生这些标题。独一首要的工作是draw是一个虚函数,何况暗中认可参数中的多个在派生类中被再一次定义了

 

 

从一初始就让大家简化此次的座谈。你有两类你能够三番一回的函数:虚函数和非虚函数。然则,重新定义贰个非虚函数总是错误的(Item 36),所以大家能够安全的把那个条约的商量限定在后续带私下认可参数值的虚函数上。

2. C++为啥不对参数举行动态绑定?

干什么C++坚定不移用一种有十分态的主意来运作?答案和平运动行时成效相关。假诺三个私下认可参数是动态绑定的,编译器就需求用一种格局在运作时为虚函数参数鲜明二个体面的默认值,比起近来在编写翻译期决定那几个参数的建制,它越来越慢特别千头万绪。做出的决定是越来越多的设想了进度和贯彻的简单性,结果是您可以大快朵颐神速的施行进程,但是只要你未曾理会到这几个条目款项的建议,你就能很吸引。

1. 虚函数是动态绑定的,而暗中同意参数是静态绑定的

在这种气象下,那几个条目款项的求证就一定直白了:虚函数是动态绑定的,而私下认可参数值是静态绑定的。

那是怎样?你说你不堪重负的脑部已经淡忘了动态绑定和静态绑定之间的界别?(为了好记,静态绑定也叫做早绑定(early binding),动态绑定也叫做晚绑定(late binding))。让我们看一下:

叁个指标的静态类型是您以往在前后相继文件中声称的体系,考虑如下的类承接种类:

 1 // a class for geometric shapes
 2 class Shape {
 3 public:
 4 enum ShapeColor { Red, Green, Blue };
 5 // all shapes must offer a function to draw themselves
 6 virtual void draw(ShapeColor color = Red) const = 0;
 7 ...
 8 };
 9 
10 class Rectangle: public Shape {
11 public:
12 // notice the different default parameter value — bad!
13 virtual void draw(ShapeColor color = Green) const;
14 ...
15 };
16 class Circle: public Shape {
17 public:
18 virtual void draw(ShapeColor color) const;
19 ...
20 };

 

画成类承袭图会是上面这么些样子:

 图片 2

前些天思量多少个指针:

1 Shape *ps;                               // static type = Shape*
2 
3 Shape *pc = new Circle;           // static type = Shape*
4 
5 Shape *pr = new Rectangle;    // static type = Shape*

 

在这么些例子中,ps,pc和pr都被声称为指向shape的指针,所以它们用Shape作为它们的静态类型。注意无论shape指针真正指向的是何等指标,静态类型都以Shape*。

 

三个指标的动态类型由指针当前本着的对象类型来支配。也便是,它的动态类型申明了它的作为会是怎么的。看上边的例证,pc的动态类型是Circle*,pr的动态类型是Rectangle*。对于ps,它实在并未有动态类型,因为它还未有引用任何对象。

 

正如字面意思所代表的,在程序运营时动态类型是能够改造的,特别是经过赋值:

1 ps = pc;      // ps’s dynamic type is now Circle* 
2 ps = pr;      // ps’s dynamic type is now Rectangle*

虚函数是动态绑定的,意味着哪个函数被调用是由发生调用的靶子的动态类型来调整的:

1 pc->draw(Shape::Red);          // calls Circle::draw(Shape::Red)
2 
3 pr->draw(Shape::Red);          // calls Rectangle::draw(Shape::Red)

 

那些都是旧文化了,小编精通您早晚了然虚函数。当您思量带默许参数值的虚函数时,麻烦出现了,因为虚函数是动态绑定的,可是暗中认可参数是静态绑定的。那意味着你或然会停下多个虚函数的调用,因为函数定义在派生类中却选用了基类中的暗中同意参数:

 

1  pr->draw();                           // calls Rectangle::draw(Shape::Red)!

 

在这种景观中,pr的动态类型是Rectangle*,所以Rectangle的虚函数被调用,那也是您所企盼的。在Rectangle::draw中,暗中同意参数值是Green。可是因为pr的静态类型是Shape*,这么些函数调用的默许参数值是源于于Shape类实际不是Rectangle类!最终的结果是这么些调用由二个意外的也差非常少是您意料不到的重组组成:也正是Shape类和Rectangle类中的draw证明混合而成。

 

Ps,pc和pr都为指针不是致使那么些标题的因由。纵然它们是援用也同样会出现这一个难点。独一首要的作业是draw是多个虚函数,并且暗中同意参数中的一个在派生类中被重复定义了

 

3. 个例探究——为基类和派生类提供同样的暗中认可参数

 

那都很好,不过看看要是如此做会产生哪些:服从这么些条目款项的鲜明还要为基类和派生类函数同期提供暗许参数:

 1 class Shape {
 2 
 3 public:
 4 
 5 enum ShapeColor { Red, Green, Blue };
 6 
 7 virtual void draw(ShapeColor color = Red) const = 0;
 8 
 9 ...
10 
11 };
12 
13 class Rectangle: public Shape {
14 public:
15 virtual void draw(ShapeColor color = Red) const;
16 ...
17 };

 

代码重复的主题材料应际而生了。更倒霉的是,与代码重复难题便随而来的代码依赖难题:假若Shape中的私下认可参数被涂改了,全体重复这些参数的派生类都须要被修改。否则重新定义承袭而来的暗中认可参数值的标题会重新出现。该如何是好?

 

当你让虚函数根据你的点子来运行时遭遇了麻烦,思量取代设计格局是很睿智的,Item 35中牵线了交替虚函数的不等措施。当中的四个是非虚接口用法(NVI idiom):用基类中的public非虚函数调用一个private虚函数,private虚函数能够在派生类中再度被定义。以往,大家用非虚函数内定默许参数,而用虚函数来加强际的干活:

 1 class Shape {
 2 public:
 3 enum ShapeColor { Red, Green, Blue };
 4 
 5 void draw(ShapeColor color = Red) const   // now non-virtual
 6 
 7 {                                                                 
 8 
 9  
10 
11 doDraw(color);                            // calls a virtual
12 
13 }                                                  
14 
15 ...                                                 
16 
17 private:                                       
18 
19 
20 virtual void doDraw(ShapeColor color) const = 0; // the actual work is
21 }; // done in this func
22 class Rectangle: public Shape {
23 public:
24 ...
25 private:
26 
27 virtual void doDraw(ShapeColor color) const; // note lack of a
28 
29 ...                                                                       // default param val.
30 
31 };               

                                            

因为非虚函数应该永久不会在派生类中被重定义(Item 36),那几个陈设保险draw的color私下认可参数应该恒久是Red。

 

2. C++为何不对参数实行动态绑定?

怎么C++坚持不渝用一种有失水准的艺术来运作?答案和周转时效能相关。假若二个暗中同意参数是动态绑定的,编写翻译器就须求用一种办法在运维时为虚函数参数鲜明三个适用的默许值,比起如今在编写翻译期决定那几个参数的机制,它越来越慢特别复杂。做出的支配是更多的设想了快慢和兑现的轻巧性,结果是您能够享用迅捷的实践进程,可是假诺你未有在意到这一个条约的提出,你就能够很吸引。

4. 总结

恒久不要再度定义三个连续而来的暗中认可参数值,因为默许参数值是静态绑定的,而虚函数——你应该重新定义的独一的函数——是动态绑定的。

3. 个例商量——为基类和派生类提供同样的私下认可参数

 

这都很好,不过看看固然那样做会发生怎么着:遵从那一个条目的确定还要为基类和派生类函数同期提供暗中认可参数:

 1 class Shape {
 2 
 3 public:
 4 
 5 enum ShapeColor { Red, Green, Blue };
 6 
 7 virtual void draw(ShapeColor color = Red) const = 0;
 8 
 9 ...
10 
11 };
12 
13 class Rectangle: public Shape {
14 public:
15 virtual void draw(ShapeColor color = Red) const;
16 ...
17 };

 

代码重复的主题素材应际而生了。更不佳的是,与代码重复难点便随而来的代码依赖难点:假诺Shape中的默许参数被修改了,全部重复那么些参数的派生类都急需被更换。不然重新定义承接而来的私下认可参数值的难点会另行出现。该怎么做?

 

当你让虚函数根据你的艺术来运行时遇上了麻烦,思量替代设计艺术是很睿智的,Item 35中介绍了替换虚函数的两样方法。在那之中的一个是非虚接口用法(NVI idiom):用基类中的public非虚函数调用三个private虚函数,private虚函数能够在派生类中重复被定义。今后,大家用非虚函数钦点私下认可参数,而用虚函数来坚实在的做事:

 1 class Shape {
 2 public:
 3 enum ShapeColor { Red, Green, Blue };
 4 
 5 void draw(ShapeColor color = Red) const   // now non-virtual
 6 
 7 {                                                                 
 8 
 9  
10 
11 doDraw(color);                            // calls a virtual
12 
13 }                                                  
14 
15 ...                                                 
16 
17 private:                                       
18 
19 
20 virtual void doDraw(ShapeColor color) const = 0; // the actual work is
21 }; // done in this func
22 class Rectangle: public Shape {
23 public:
24 ...
25 private:
26 
27 virtual void doDraw(ShapeColor color) const; // note lack of a
28 
29 ...                                                                       // default param val.
30 
31 };               

                                            

因为非虚函数应该永世不会在派生类中被重定义(Item 36),那一个设计有限援助draw的color暗中同意参数应该长久是Red。

 

4. 总结

世代不要再度定义二个后续而来的暗中同意参数值,因为暗许参数值是静态绑定的,而虚函数——你应当重新定义的并世无双的函数——是动态绑定的。

effective c++ Item 37 永久不要再度定义承继而来的函数暗中同意参数值,effectiveitem 从一初阶就让大家简化此次的探究。你有两类你能够继...

编辑:计算机网络 本文来源:读书笔记,永远不要重新定义继承而来的函数默

关键词: