C++中几个值得分析的小问题

下面3个小问题都是我认为C++ Beginner应该能够解答或辨别清楚的。希望我们能通过题目挖掘更多的信息,而不仅仅局限在解题。我最喜欢说的话:能力有限,所以作为抛砖引玉,希望共同讨论,指出错误。

另外,我都是碰到一个觉得有必要记录的问题,就写下来说说,所以每一篇内容可能不是单一主题。

1、先来看一道简单题目。有下面这个继承类:


  1. class Person 
  2. public: 
  3. void Walk() //普通人的“走” 
  4. cout << "Person::Walk I am an Ordinary People." << endl; 
  5. }; 
  6. }; 
  7.  
  8. class Student : public Person 
  9. public: 
  10. void Walk() //学生的“走” 
  11. cout << "Student::Walk I am a student." << endl; 
  12. }; 
  13. }; 

你没看错Walk()是非虚函数。请解释下面代码:


  1. Student s; 
  2. Person* pp = &s; 
  3. pp->Walk(); 
  4.  
  5. Student* ps= &s; 
  6. ps->Walk(); 

结果是这样的:

分析:Walk()是非虚函数,被静态绑定所限制,所以pp、ps是什么类型就决定了调用的版本。这里,我还要说明的一点是:明白接口继承和实现继承。声明一个non-virtual函数的目的是为了令derived
class继承函数的接口及一份强制性实现。所以,绝不要重新定义继承而来的non-virtual函数。

2、下面这个问题实质上也是静态绑定与动态绑定的问题,但看起来不那么明显。


  1. class Shape 
  2. public: 
  3. enum ShapeColor{Red, Green, Blue}; //形状颜色 
  4.  
  5. virtual void Draw(ShapeColor color = Red) const = 0; 
  6. }; 
  7.  
  8. class Circle : public Shape 
  9. public: 
  10. virtual void Draw(ShapeColor color) const 
  11. cout << "I am Circle::Draw. "; 
  12. cout << "My color = " << color << endl; 
  13. }; 
  14.  
  15. class Rectangle : public Shape 
  16. public: 
  17. virtual void Draw(ShapeColor color = Green) const //缺省的参数值被更改了 
  18. cout << "I am Rectangle::Draw. "; 
  19. cout << "My color = " << color << endl; 
  20. }; 

我主要想说两个问题。

(1)当你下面这样调用时,请解释会发生什么情况。


  1. Circle cr; //(1) 编译不通过 
  2. cr.Draw(); 
  3. Shape *ps = &cr; //(2) 
  4. ps->Draw(); 

没错,(1)通过对象调用而不指定参数是错误的,而(2)的结果是这样的:color = 0代表Red这你应该是知道的。

分析:通过对象调用是静态绑定,一定要指定参数值,因为静态绑定这个函数不从base class继承缺省参数值。动态绑定却可以从base class继承参数值。注意,这里我就不强调动态绑定和静态绑定的概念了,但下面这个一定是静态绑定:


  1. Circle cr; 
  2. Circle *ps = &cr; //这还是静态绑定,静态类型Circle *,编译不通过 
  3. ps->Draw(); 

(2)第二个我想说的问题,请解释下面的调用结果。


  1. Shape* ps1 = new Rectangle; 
  2. ps1->Draw(); 
  3. Shape* ps2 = new Circle; 
  4. ps2->Draw(); 

是这样令人可喜的结果:

你是说,你在Rectangle中已经将Draw的缺省值改为1(Green)了,怎么没效果?

分析:Rectangle::Draw的缺省参数值为GREEN,但ps2的静态类型为Shape*,所以此调用的缺省参数值来自Shape class。

如果你非要让Rectangle::Draw的参数有所改变,可以这样调用(提供参数):


  1. Shape* ps4 = new Rectangle; 
  2. ps4->Draw(Shape::Green); 
  3. Shape* ps5 = new Circle; 
  4. ps5->Draw(Shape::Green); 

这个问题是想提醒你:virtual函数是动态绑定,缺省参数值是静态绑定。所以,不应该重新定义这个缺省参数值。

3、多重继承为什么会含有多个虚表指针而不是一个?

这道题是我看一位同学面试经验时,面试官提的,我试着回答一下,不知道在不在点子上,还请补充和指正。

答:多重继承下,因为编译器对一个derived class实现了n-1个虚表,n表示上一层base class的个数,当然假设每个base

class都有至少有一个virtual函数,否则编译器是不会为其添加vptr和vtbl了。所以说有多少个虚表,自然就有多少个指针指向,而不是一个。

这样说我不知道合理不合理,可能面试官要问的点是“为什么需要多个虚表?一个虚表行不行?”

这个属于编译器厂商做的事情,标准并未规范。C++的父亲就做出过这样的一款编译器原型,通过增大vtbl的体积,每个slot上不只有一个指针,还有一个offset,用来调整this指针的指向。

这样做的弊端是:所有vtbl中的虚函数指针都包含这样一个offset,并且假设不需要调整this指向,调用时还是要做offset的加法操作,尽管offset此时为0。另外,vtbl中每个slot体积的膨胀。这些都是效率问题。

实际上,用来调整this的指向用的比较多的是trunk技术,必须以汇编编写才能获得高效率。另外,sun编译器就是把多个虚表连锁为1个,每个表格中含有下一个表格的指针(通过offset方式),这样就需要一个指针就好了。

理解能力有限,不知道问的是不是这么一回事?

来源:51CTO

时间: 2024-11-03 06:19:52

C++中几个值得分析的小问题的相关文章

Empire中的Invoke-WScriptBypassUAC利用分析

本文讲的是Empire中的Invoke-WScriptBypassUAC利用分析,知名的后渗透测试框架Empire是一个很好的学习模板,其中包含的后渗透技巧很值得深入研究. 本文将要挑选Empire中一个经典的UAC绕过方法Invoke-WScriptBypassUAC进行分析,介绍绕过原理以及在渗透测试中的更多利用技巧.知道如何利用,才能知道如何防御. 0x01 简介 本文将要介绍如下内容: · Invoke-WScriptBypassUAC绕过原理 · 利用扩展 · 防御检测 0x02 In

网页中图片幻灯片用户体验设计小细节

图片幻灯片是网页中最常见的一种效果,功能几乎差别不大,可很多网页设计师可能都没有考虑过这种最常用的幻灯片其中所涵盖的用户体验思想,本文通过国内外几家大型网站中的幻灯片来分析他们各自的设计小细节及给我们带来的体验 1.卓越亚马逊的首页轮换图片,每刷新一次,都是随机不同的顺序显示,这样的设计解决了对于较多图片轮换而靠后的图片信息很少被看到的问题,这点对于电子商务等时效性不是很讲究的网站来说尤其重要: 2.最初的时候是点击照片跳转到下一张,再然后是点击照片的左边是上一张,点击照片右边是下一张,当我们习

网站内容分析中的路径如何分析

一,网站路径分析的目的与问题 与所有的分析工作开始时一样,在对网站进行路径分析之前,我们需要先确定这次分析的目的.我们希望从这次分析中收获哪些结果?发现哪些问题?或者是验证哪些之前的假设或判断? 这些明确的分析目的会帮助我们选择分析时所需要的具体指标和维度,以及具体的分析方法.如果你对于分析结果没有非常明确的目的,那么多数情况下只会看到一个粗略的网站路径统计数据,幸运的话可能会发现一些访问者在网站中流动的规律,但这并不能带来有价值的结论和建议.更多的时候,这些网站路径统计数据本身可能根本就不准确

C/C++的浮点数在内存中的存储方式分析及实例_C 语言

C/C++的浮点数在内存中的存储方式分析 任何数据在内存中都是以二进制的形式存储的,例如一个short型数据1156,其二进制表示形式为00000100 10000100.则在Intel CPU架构的系统中,存放方式为  10000100(低地址单元) 00000100(高地址单元),因为Intel CPU的架构是小端模式.但是对于浮点数在内存是如何存储的?目前所有的C/C++编译器都是采用IEEE所制定的标准浮点格式,即二进制科学表示法.        在二进制科学表示法中,S=M*2^N 主

谈谈备案过程中容易碰到的几个小问题

http://www.aliyun.com/zixun/aggregation/8456.html">网站备案是根据国家法律法规需要网站的所有者向国家有关部门申请的备案,现在主要有ICP备案和公安局备案. 公安局备案一般按照各地公安机关指定的地点和方式进行. ICP备案可以自主通过官方备案网站 http://www.miibeian.gov.cn 在线备案或者通过当地电信部门两种方式来进行备案. 网站备案的目的就是为了防止在网上从事非法的网站经营活动,打击不良互联网信息的传播,如果网站不备

Java 编程技术中汉字问题的分析及解决(转)

编程|汉字|解决|问题 Java 编程技术中汉字问题的分析及解决 段明辉自由撰稿人2000 年 11月 8日内容: 汉字编码的常识 Java 中文问题的初步认识 Java 中文问题的表层分析及处理 Java 中文问题的根源分析及解决 Java Servlet 中文问题的根源 修改 Servlet.jar 中文乱码的处理函数 参考资料 作者简介在基于 Java 语言的编程中,我们经常碰到汉字的处理及显示的问题.一大堆看不懂的乱码肯定不是我们愿意看到的显示效果,怎样才能够让那些汉字正确显示呢?Jav

Java 编程技术中汉字问题的分析及解决(转自IBM)

编程|汉字|解决|问题 Java 编程技术中汉字问题的分析及解决 段明辉自由撰稿人2000 年 11月 8日 在基于 Java 语言的编程中,我们经常碰到汉字的处理及显示的问题.一大堆看不懂的乱码肯定不是我们愿意看到的显示效果,怎样才能够让那些汉字正确显示呢?Java 语言默认的编码方式是UNICODE ,而我们中国人通常使用的文件和数据库都是基于 GB2312 或者 BIG5 等方式编码的,怎样才能够恰当地选择汉字编码方式并正确地处理汉字的编码呢?本文将从汉字编码的常识入手,结合 Java 编

javascript中eval函数用法分析

  javascript中eval函数用法分析         这篇文章主要介绍了javascript中eval函数用法,实例分析了javascript中eval函数的使用技巧,非常具有实用价值,需要的朋友可以参考下 本文实例分析了javascript中eval函数用法.分享给大家供大家参考.具体分析如下: eval()只有一个参数,如果传入的参数不是字符串,则直接返回这个参数.否则会将字符串当成js代码进行编译,如果编译失败则抛出语法错误(SyntaxError)异常.如果编译成功则开始执行这

python中as用法实例分析

  这篇文章主要介绍了python中as用法,实例分析了as的功能及相关使用技巧,非常具有实用价值,需要的朋友可以参考下 本文实例讲述了python中as用法.分享给大家供大家参考.具体分析如下: ? 1 import some # some 为一个模组 如果想要改变被导入模组在当前模组中的名称,而不是sys.modules中的名称.可以使用import as,例如: ? 1 2 import some as other print(other.name) 和 ? 1 2 3 4 import