因为又一次定义继承而来的non-virtual函数是不对的(见上一个条款),所以这个条款就将问题局限于:绝不又一次定义继承一个带有缺省參数值的virtual函数。

(一)

virtual函数是动态绑定的,而缺省參数却是静态绑定。

对象的所谓静态类型,是它在程序中被声明时所採用的类型。

你可能会在“调用一个定义于derived
class 内的virtual函数”的同一时候,却使用了base
class为它所指定的缺省參数值。

(二)

为什么继承而来的virtual函数的缺省參数值不能被又一次定义呢?

事实上原因也挺简单:缺省參数是静态绑定,而virtual函数是动态绑定. 所谓对象的静态绑定也叫前期绑定,它是说该对象类型和行为在程序编译期间就能够确定,比如:

class Shape{
public:
enum Color{RED,GREEN,BLUE};
virtual void draw(Color color = RED)const = 0;
...
};
class Circle : public Shape{
public:
//哦欧! 居然改变缺省參数值
virtual void draw(Color color = GREEN)const{ ... }
};
class Rectangle : public Shape{
public:
//没用指定參数类型,须要用户去明白的指定其值
//静态绑定下不继承基类的缺省值,若以指针或引用调用则不须要指定缺省值,由于动态绑定
//继承基类的參数缺省值
virtual void draw(Color color)const{ ... }
};

看一下以下几个指针:

 Shape* ps;
Shape* pc = new Circle;
Shape* pr = new Rectangle;

这里的ps,pc,pr无论它详细指向的是什么对象,他们的静态类型都是Shape*。而动态类型就是它们真正指向的对象的类型。故pc的动态类型为Circle*,而pr的动态类型为Rectangle*,ps因为没有指向不论什么对象,所以此时没有动态类型。

(三)看以下这个语句!

pc->draw();   //注意调用的是: Circle::draw(RED)

怎么会调用Circle::draw(RED)呢!?为什么不是Circle::draw(GREEN)?

原因:

(1)首先依据其调用语句用指针这一事实,我们就知道了其调用的版本号应该是该指针的动态类型的函数版本号,即Circle::draw,这个问题不大。

(2)以下我们来看它的传值參数,前面我们提到缺省參数值是静态绑定的,而pc的静态类型是Shape*,所以该參数的传入值是Shape的该函数版本号的缺省值。

那为什么C++坚持以这种乖张的方式来运作呢?答案在于执行期效率,假设缺省值也是动态绑定的,那么编译期就必需要有办法在执行期为virtual函数决定适当的參数缺省值.假设这样做的话,就要比眼下实现的"在编译期决定"的机制更慢并且更复杂,考虑到执行速度和实现上的简易性,C++放弃了这种做法。

(四)解决方法!

如今,为了遵循本款约定却同一时候提供缺省參数值给你的基类和父类,,代码就这样了:

class Shape{
public:
enum Color{RED,GREEN,BLUE};
virtual void draw(Color color = RED)const = 0;
...
};
class Circle:public Shape{
public:
virtual void draw(Color color = RED)const {...}
};

明显的是代码反复嘛!何况你要是想改变缺省值的话,必需要同一时候改变基类和子类函数的缺省值,一不小心,就会出现漏改或写错的情况,导致意想不到的错误出现.有没用一种更方便的写法呢?当然,你还记得NVI手法吗?额..,(non-virtual
interface),要是忘记的话,回过头看看条款35,用这样的手法的话,我们写下代码例如以下:

class Shape{
public:
enum Color{RED,GREEN,BLUE};
void draw(Color color = RED) const{
...
doDraw(color);
...
}
...
private:
virtual void doDraw(Color color) const = 0;
};
class Circle:public Shape{
...
private:
virtual void doDraw(Color color){ ... }
};

因为draw是non-virtual而non-virtual绝对不会被又一次改写(条款36),所以color的缺省值总是为RED。

请记住:

(1)绝对不要又一次定义一个继承而来的缺省參数值,由于缺省參数值都是静态绑定,而virtual函数-你唯一应该覆写的东西-却是动态绑定。

Effective C++:条款37:绝不又一次定义继承而来的缺省參数值的更多相关文章

  1. Effective C++ Item 37 绝不又一次定义继承而来的缺省參数值

    本文为senlie原创,转载请保留此地址:http://blog.csdn.net/zhengsenlie 经验:绝对不要又一次而来的缺省參数值.由于缺省參数值都是静态绑定,而 virtual 函数 ...

  2. effective C++中条款37:绝不又一次定义继承而来的缺省參数值

    virtual 函数会动态绑定,而virtual函数的缺省參数值是静态绑定的. 用一个base类型的指针p去指向一个derived类对象.通过p调用虚函数时,会动态绑定到实际所指对象中的函数:用一个d ...

  3. Effective C++ Item 36 绝不又一次定义继承而来的 non-virtual 函数

    本文为senlie原创.转载请保留此地址:http://blog.csdn.net/zhengsenlie 经验:绝对不要又一次定义继承而来的 non-virtual 函数 --> Item 7 ...

  4. 《effective C++》:条款37——绝不重新定义继承而来的缺省参数值

    引子: 阿里的一道题: #include <IOSTREAM> using namespace std; class A{ public: ) { cout<<"a~ ...

  5. Effective C++ -----条款37:绝不重新定义继承而来的缺省参数值

    绝对不要重新定义一个继承而来的缺省参数值,因为缺省参数值都是静态绑定,而virtual函数-----你唯一应该覆写的东西-----却是动态绑定.

  6. Effective C++ -----条款32:确定你的public继承塑模出is-a关系

    “public继承”意味is-a.适用于base classes身上的每一件事情一定也适用于derived classes身上,因为每一个derive class对象也都是一个base class对象 ...

  7. 条款37:绝不重新定义继承而来的缺省参数值(Never redefine a function's inherited default parameter value)

    NOTE: 1.绝不重新定义一个继承而来的缺省参数值,因为缺省参数值都是静态绑定的,而virtual 函数-----你唯一应该覆盖的东西----却是动态绑定的.

  8. More Effective C++ 条款0,1

    More Effective C++ 条款0,1 条款0 关于编译器 不同的编译器支持C++的特性能力不同.有些编译器不支持bool类型,此时可用 enum bool{false, true};枚举类 ...

  9. [More Effective C++]条款22有关返回值优化的验证结果

    (这里的验证结果是针对返回值优化的,其实和条款22本身所说的,考虑以操作符复合形式(op=)取代其独身形式(op),关系不大.书生注) 在[More Effective C++]条款22的最后,在返回 ...

随机推荐

  1. ImageMagick 转换 progressive jpeg

    什么是渐进式图片(Progressive JPEG)? 来自 张鑫旭-鑫空间-鑫生活 的解释: 不知诸位有没有注意到,这些jpg格式的图片在呈现的时候,有两种方式,一种是自上而下扫描式的,还有一种就是 ...

  2. margin 属性的相关问题

    1.margin 的IE6 双边距问题 问题描述:浮动的块挨边框的时候会产生双倍的边距 解决方案: 1.增加display:inline; 2.去除float属性 2.margin 的重叠问题 CSS ...

  3. jsp 有哪些内置对象?作用分别是什么? 分别有什么方 法?

    JSP共有以下9种基本内置组件 request对象          客户端请求,此请求包含来自GET/POST的请求参数,通过它才能了解到客户的需求,然后做出相应. response对象       ...

  4. JavaSE学习总结第26天_网络编程

      26.01  网络编程概述 网络编程:就是用来实现网络互连的不同计算机上运行的程序间可以进行数据交换. 26.02  网络模型概述和图解 计算机网络之间以何种规则进行通信,就是网络模型研究问题. ...

  5. 我用过的linux命令--安装Hadoop

    1. hadoop软件传送给虚拟机 还是利用WinSCP把hadoop软件安装包,放到linux的Downloads文件夹中. 2. 选择安装目录 把hadoop安装包copy到这个安装目录中,这里我 ...

  6. SQL Server配置管理WMI问题

       今天在打开数据库的时候,连接不上.一看错误就知道肯定是SQL Server的服务没开启,所以自然而然的去SQL Server配置管理中去打开,但是打开配置管理器的时候出现了下面的错误:      ...

  7. contains 和 ele.compareDocumentPosition确定html节点间的关系

    ~~~ nodeA.contains(nodeB) //ie ,   nodeA.compareDocumentPosition(nodeB) //firefox opera 1.DOMElement ...

  8. 基于Visual C++2013拆解世界五百强面试题--题5-自己实现strstr

    请用C语言实现字符串的查找函数strstr, 找到则返回子字符串的地址,没有找到返回为空,请用数组操作与指针操作实现 看到题目想到最简单的方法就是母字符串和子字符串比较,如果不同,将指向母字符串的指针 ...

  9. C++ 用libcurl库进行http 网络通讯编程

      一.LibCurl基本编程框架libcurl是一个跨平台的网络协议库,支持http, https, ftp, gopher, telnet, dict, file, 和ldap 协议.libcur ...

  10. android 之Fragment(官网资料翻译)

    原文地址: http://blog.csdn.net/lilu_leo/article/details/7671533 ***************************  正文分割线 ***** ...