C&C++面试题之四(每次10题)

  1. 简单描述一下虚继承和虚基类概念
    • struct CSubClass : public virtual CBase {};
    • 其中CBase称之为CSubClass的虚基类,而不是说CBase就是个虚基类,因为CBase还可以不不是虚继承体系中的基类。
    • C++中,virtual的意义和它的英语本意是一样的,也就是它所修饰的东西本质上是存在的,但是没有直观的形式表现。不可以在语言模型中直接调用或体现的,但是确实是存在可以被间接的方式进行调用或体现的。比如:虚函数必须要通过一种间接的运行时(而不是编译时)机制才能够激活(调用)的函数,而虚继承也是必须在运行时才能够进行定位访问的一种体制。存在,但间接。
  2. 如何理解虚函数中的virtual存在、间接、共享这三个特征
    • 存在性:函数是客观存在的,只是在编译的时候没有确定具体函数入口,在具体的类实例中才确定
    • 间接性:表明了他必须在运行时根据实际的对象来完成函数寻址。
    • 共享性:表现在基类会共享被子类重载后的虚函数,指向相同的函数入口。
  3. 如何理解虚继承中virtual存在、间接、共享这三个特征
    • 存在性:虚继承体系和虚基类确实存在, 也就是说在对象内存中必须要包含虚基类的完整子对象,以便能够完成通过地址完成对象的标识。那么至于虚基类的子对象会存放在对象的那个位置(头、中间、尾部)则由各个编译器选择,没有差别。(在VC8中无论虚基类被声明在什么位置,虚基类的子对象都会被放置在对象内存的尾部)
    • 间接性:表明了在访问虚基类的成员时同样也必须通过某种间接机制来完成, 间接性表明了在直接虚继承子类中一定包含了某种指针(偏移或表格)来完成通过子类访问虚基类子对象(或成员)的间接手段(因为虚基类子对象是共享的,没有确定关系),至于采用何种手段由编译器选择。(在VC8中在子类中放置了一个虚基类指针vbc,该指针指向虚函数表中的一个slot,该slot中存放着虚基类子对象的偏移量的负值,实际上就是个以补码表示的int类型的值,在计算虚基类子对象首地址时,需要将该偏移量取绝对值相加,这个主要是为了和虚表中只能存放虚函数地址这一要求相区别,因为地址是原码表示的无符号int类型的值)
    • 共享性:表现在虚基类会在虚继承体系中被共享,而不会出现多份拷贝,并且通过某种间接的机制来完成共享的引用关系
  4. 为什么继承体系中只要出现了虚继承,都要显式书写初始化过程
    • 一旦出现了虚基类,就必须在每一个继承类中都必须包含虚基类的初始化语句。
    • 虚基类是被共享的,也就是在继承体系中无论被继承多少次,对象内存模型中均只会出现一个虚基类的子对象(这和多继承是完全不同的),这样一来既然是共享的那么每一个子类都不会独占,但是总还是必须要有一个类来完成基类的初始化过程(因为所有的对象都必须被初始化,哪怕是默认的),同时还不能够重复进行初始化,那到底谁应该负责完成初始化呢?C++标准中(也是很自然的)选择在每一次继承子类中都必须书写初始化语句(因为每一次继承子类可能都会用来定义对象),而在最下层继承子类中实际执行初始化过程。所以上面在每一个继承类中都要书写初始化语句,但是在创建对象时,而仅仅会在创建对象用的类构造函数中实际的执行初始化语句,其他的初始化语句都会被压制不调用。
  5. 虚继承对性能有什么影响
    • 由于有了间接性和共享性两个特征,所以决定了虚继承体系下的对象在访问时必然会在时间和空间上与一般情况有较大不同。
      • 时间:在通过继承类对象访问虚基类对象中的成员(包括数据成员和函数成员)时,都必须通过某种间接引用来完成,这样会增加引用寻址时间(就和虚函数一样),其实就是调整this指针以指向虚基类对象,只不过这个调整是运行时间接完成的。 (在VC8中通过打开汇编输出,可以查看*.cod文件中的内容,在访问虚基类对象成员时会形成三条mov间接寻址语句,而在访问一般继承类对象时仅仅只有一条mov常量直接寻址语句)
      • 空间:由于共享所以不同在对象内存中保存多份虚基类子对象的拷贝,这样较之多继承节省空间。
  6. 虚拟继承有哪些应用场景
    • 这个问题其实很难有答案,一般情况下如果你确定出现多继承没有必要,必须要共享基类子对象的时候可以考虑采用虚继承关系(C++标准ios体系就是这样的)。由于每一个继承类都必须包含初始化语句而又仅仅只在最底层子类中调用,这样可能就会使得某些上层子类得到的虚基类子对象的状态不是自己所期望的(因为自己的初始化语句被压制了),所以一般建议不要在虚基类中包含任何数据成员(不要有状态),只可以作为接口类来提供。
  7. 多重继承如何消除向上继承的二义性(继承的模糊性)
    • 使用虚拟继承即可.虚拟继承中的虚基类永远只有一个实例化的对象。也就是上面几个问题中说到的共享性
    • 注意:虚拟继承和虚函数有一定相似的地方,但是他们绝对是没有任何联系的
    • ORM:object ralational mapping对象关系映射
    • 如果一个类有虚函数,那么他就会有一个虚函数表,虚函数表就是一个数组,每个单元指向一个虚函数地址
  8. 非C++内建型别 A 和 B,在哪几种情况下B能隐式转化为A?

    • class B : public A { ……} // B公有继承自A,可以是间接继承的
    • class B { operator A( ); } // B实现了隐式转化为A的转化
    • class A { A( const B& ); } // A实现了non-explicit的参数为B(可以有其他带默认值的参数)构造函数
    • A& operator= ( const A& ); // 赋值操作,虽不是正宗的隐式类型转换,但也可以勉强算一个
  9. 说说使用数组名和指针时的几点注意事项

    • 当使用sizeof( )操作数组时,返回的是整个数组的大小
    • 当数组名作为参数传递进函数的时候,数组名退化为指针
  10. 在什么时候需要使用“常引用”?

    • 如果既要利用引用提高程序的效率,又要保护传递给函数的数据不在函数中被改变,就应使用常引用。常引用声明方式:const 类型标识符 &引用名=目标变量名;
    • int a ;
      const int &ra=a;
      ra=1; //错误
      a=1; //正确
      例2
      string foo( );
      void bar(string & s);
      那么下面的表达式将是非法的:
      bar(foo( ));
      bar("hello world");
    • 原因在于foo( )和"hello world"串都会产生一个临时对象,而在C++中,这些临时对象都是const类型的。因此上面的表达式就是试图将一个const类型的对象转换为非const类型,这是非法的。引用型参数应该在能被定义为const的情况下,尽量定义为const 。
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值