一. sizeof计算结构体

  注:本机机器字长为64位

1.最普通的类和普通的继承

#include<iostream>
using namespace std; class Parent{
public:
void fun(){
cout<<"Parent fun"<<endl;
}
}; class Child : public Parent{
public:
void fun(){
cout<<"Child fun"<<endl;
}
char ch[];
}; int main(){
Parent p;
Child ch;
cout<<"Parent size : "<<sizeof(p)<<", Child size : "<<sizeof(ch) <<endl;
return ;
} /*
  运行结果:
Parent size : 1, Child size : 5
*/

  分析:那么为什么类(对象)的大小为什么会是1个字节呢?那是被编译器插进去的一个char ,使得这个class的不同实体(object)在内存中配置独一无二的地址。也就是说这个char是用来标识类的不同对象的。因为如果不是1,当定义这个类的对象数组时候A objects[5]; objects[0]和objects[1]就在同一个地址处,就无法区分。

2.基类中含有私有成员

#include<iostream>
using namespace std; class Parent{
public:
void fun(){
cout<<"Parent fun"<<endl;
}
private:
int x;
}; class Child : public Parent{
public:
void fun(){
cout<<"Child fun"<<endl;
}
char ch[];
}; int main(){
Parent p;
Child ch;
cout<<"Parent size : "<<sizeof(p)<<", Child size : "<<sizeof(ch) <<endl;
return ;
}
/*
  执行结果:
  Parent size : 4, Child size : 12
*/

  分析:基类里的私有成员在派生类里仍占有内存。在派生类里,基类的int占4个字节,char ch[5]占用5个字节,考虑内存的对齐,变成4+(5+3)=12个字节。

3.类中含有虚函数

#include<iostream>
using namespace std; class Parent{
public:
virtual void fun(){
cout<<"Parent fun"<<endl;
}
}; class Child : public Parent{
public:virtual void hjzgg(){
cout<<"呵呵"<<endl;
}
char ch[];
}; int main(){
Parent p;
Child ch;
cout<<"Parent size : "<<sizeof(p)<<", Child size : "<<sizeof(ch) <<endl;
return ;
}
/*
  执行结果:
  Parent size : 8, Child size : 16
*/

  分析:有虚函数的类有个virtual table(虚函数表),里面包含了类的所有虚函数,类中有个virtual table pointers,通常成为vptr指向这个virtual table,占用8个字节的大小。成员类Child public继承于Parent,类Child的虚函数表里实际上有两个虚函数Parent::fun()和Child::hjzgg(),类B的大小等于char ch[5]的大小加上一个指向虚函数表指针vptr的大小,考虑内存对齐为16。一个类里若有虚函数,无论有多少个虚函数都只有一个指向虚表的指针,虚表中的每一个表项保存着一个虚函数的入口地址。当调用虚函数时,先找到虚表中它对应的表项,找到入口地址再执行。

4.多重继承

#include<iostream>
using namespace std; class Parent{
public:
virtual void fun(){
cout<<"Parent fun"<<endl;
}
}; class Father{
public:
virtual void fun(){
cout<<"Father fun"<<endl;
}
}; class Child : public Parent, public Father{
public:
virtual void hjzgg(){
cout<<"呵呵"<<endl;
}
char ch[];
}; int main(){
Parent p;
Child ch;
cout<<"Parent size : "<<sizeof(p)<<", Child size : "<<sizeof(ch) <<endl;
return ;
}
/*
  执行结果:
  Parent size : 8, Child size : 24
*/

  分析:Child中除了char ch[5]这5个字节,Child现有一个虚函数表,里边有Child自身定义的虚函数以及从Parent中继承过来的虚函数,然后又另一张虚函数表来存放Father中过来的虚函数,也就是Child对应两个虚函数表的指针。总共内存空间5+8+8=21,考虑内存的对齐,为24字节。

5.虚继承

  C++虚拟继承

  ◇概念:

     C++使用虚拟继承(Virtual Inheritance),解决从不同途径继承来的同名的数据成员在内存中有不同的拷贝造成数据不一致问题,将共同基类设置为虚基类。          这时从不同的路径继承过来的同名数据成员在内存中就只有一个拷贝,同一个函数名也只有一个映射。

  ◇解决问题:

  解决了二义性问题,也节省了内存,避免了数据不一致的问题。 
 同义词: 
  虚基类(把一个动词当成一个名词而已)
  当在多条继承路径上有一个公共的基类,在这些路径中的某几条汇合处,这个公共的基类就会产生多个实例(或多个副本),若只想保存这个基类的一个实例,可以将这个   公共基类说明为虚基类。

  ◇执行顺序

     首先执行虚基类的构造函数,多个虚基类的构造函数按照被继承的顺序构造;

   执行基类的构造函数,多个基类的构造函数按照被继承的顺序构造;

   执行成员对象的构造函数,多个成员对象的构造函数按照申明的顺序构造;

   执行派生类自己的构造函数;

     析构以与构造相反的顺序执行;

  注:

    从虚基类直接或间接派生的派生类中的构造函数的成员初始化列表中都要列出对虚基类构造函数的调用。但只有用于建立对象的最派生类的构造函数调用虚基类           的构造函数,而该派生类的所有基类中列出的对虚基类的构造函数的调用在执行中被忽略,从而保证对虚基类子对象只初始化一次。

    在一个成员初始化列表中同时出现对虚基类和非虚基类构造函数的调用时,虚基类的构造函数先于非虚基类的构造函数执行。

5.1 没有用虚拟继承

#include<iostream>
using namespace std; class Parent{
public:
virtual void fun(){
cout<<"Parent fun"<<endl;
}
}; class Father{
public:
virtual void fun(){
cout<<"Father fun"<<endl;
}
}; class Child : public Parent, public Father{
public:
virtual void hjzgg(){
cout<<"呵呵"<<endl;
}
char ch[];
}; int main(){
Parent p;
Child ch;
ch.fun();

    ch.Parent::fun();//这样调用是对的
    ch.Father::fun();

    cout<<"Parent size : "<<sizeof(p)<<", Child size : "<<sizeof(ch) <<endl;
return ;
}
/*
  执行结果:
[Error] request for member 'fun' is ambiguous
*/

   分析:因为派生类中的虚函数表会继承来自各个基类的虚函数。所以Child对应的虚函数表中会有Parent 和 Father各自的fun()函数,所以在调用的时候就会出现歧义,不知道应该调用哪个!

  同样,和下面一样的写法也是错误的,增加一个Super类。

#include<iostream>
using namespace std; class Super{
public:
virtual void fun(){
cout<<"Super fun"<<endl;
}
}; class Parent : public Super{
public: }; class Father : public Super{
public: }; class Child : public Parent, public Father{
public:
virtual void hjzgg(){
cout<<"呵呵"<<endl;
}
char ch[];
}; int main(){
Parent p;
Child ch;
ch.fun();
cout<<"Parent size : "<<sizeof(p)<<", Child size : "<<sizeof(ch) <<endl;
return ;
}
/*
  执行结果:
[Error] request for member 'fun' is ambiguous
*/

5.2 使用虚拟继承#include<iostream> 

using namespace std;

class Super{
public:
Super(){
cout<<"Super construction"<<endl;
}
virtual void fun(){
cout<<"Super fun"<<endl;
}
}; class Parent : virtual public Super{
public:
Parent(){
cout<<"Parent construction"<<endl;
}
}; class Father : virtual public Super{
public:
Father(){
cout<<"Father construction"<<endl;
}
}; class Child : public Parent, public Father{
public:
virtual void hjzgg(){
cout<<"呵呵"<<endl;
}
char ch[];
}; int main(){
Child ch;
ch.fun();
cout<<"Parent size : "<<sizeof(Parent)<<", Child size : "<<sizeof(Child) <<endl;
return ;
}
/*
  执行结果:
  

  Super construction
   Parent construction
   Father construction
   Super fun
   Parent size : 8, Child size : 24

*/

  分析:

  1.在多继承情况下,虚基类关键字的作用范围和继承方式关键字相同,只对紧跟其后的基类起作用。
  2.声明了虚基类之后,虚基类在进一步派生过程中始终和派生类一起,维护同一个基类子对象的拷贝。(Super的构造函数只执行了一次,如果不是有虚基类,那么Super的构造函数     将会执行两次。)
  3.观察类构造函数的构造顺序,拷贝也只有一份。

二. c++重载、覆盖、隐藏的区别和执行方式

  1.成员函数被重载的特征
  (1)相同的范围(在同一个类中); 
  (2)函数名字相同; 
  (3)参数不同; 
  (4)virtual 关键字可有可无。 
  2.“覆盖”是指派生类函数覆盖基类函数,特征是:
  (1)不同的范围(分别位于派生类与基类); 
  (2)函数名字相同; 
  (3)参数相同; 
  (4)基类函数必须有virtual 关键字。 
  3.“隐藏”是指派生类的函数屏蔽了与其同名的基类函数,特征是:

  (1)不同的范围(分别位于派生类与基类);

  (2)如果派生类的函数与基类的函数同名,但是参数不同,此时,不论有无virtual关键字,基类的函数将被隐藏(注意别与重载混淆)。 
  (3)如果派生类的函数与基类的函数同名,且参数相同,但是基类函数没有virtual 关键字。此时,基类的函数被隐藏(注意别与覆盖混淆)。

  小结:说白了就是如果派生类和基类的函数名和参数都相同,属于覆盖,这是可以理解的吧,完全一样当然要覆盖了;如果只是函数名相同,参数并不相同,则属 于隐藏。

c++面试常用知识(sizeof计算类的大小,虚拟继承,重载,隐藏,覆盖)的更多相关文章

  1. sizeof求类的大小

    用sizeof求类的大小,http://blog.csdn.net/szchtx/article/details/10254007(sizeof浅析(三)——求类的大小),这篇博文给出了非常详尽的举例 ...

  2. MySQL JDBC常用知识,封装工具类,时区问题配置,SQL注入问题

    JDBC JDBC介绍 Sun公司为了简化开发人员的(对数据库的统一)操作,提供了(Java操作数据库的)规范,俗称JDBC,这些规范的由具体由具体的厂商去做 对于开发人员来说,我们只需要掌握JDBC ...

  3. C++ 类的继承、虚拟继承、隐藏、占用空间

    主函数: #include <iostream> #include "test.h" #include "testfuc.h" using name ...

  4. c/c++面试12-18------关与sizeof那些事儿

    12 使用sizeof计算普通变量所占空间大小 (1)不同数据类型所占字节数不同(32位 64位系统不同) int----->4 double----->8 char-------> ...

  5. Java && Python 算法面试常用类以及方法总结

    数据结构 逻辑结构上: 包括集合,线性结构,非线性结构. 存储结构: 顺序存储,链式存储,索引存储,散列存储. Java 常见数据结构 大专栏  Java && Python 算法面试 ...

  6. Unity游戏开发面试基础知识

    面试第一次知识总结: 一.Unity基本操作 1.unity提供哪几种光源? 点光源.平行光.聚光灯.区域光. 2.物体发生碰撞的必要条件什么? 两个物体必须有碰撞体Collider组件,一个物体上必 ...

  7. Java面试必备知识

    JAVA面试必备知识 第一,谈谈final, finally, finalize的区别. 第二,Anonymous Inner Class (匿名内部类) 是否可以extends(继承)其它类,是否可 ...

  8. javascript常用知识点集

    javascript常用知识点集 目录结构 一.jquery源码中常见知识点 二.javascript中原型链常见的知识点 三.常用的方法集知识点 一.jquery源码中常见的知识点 1.string ...

  9. Java常用的加密解密类(对称加密类)

    Java常用的加密解密类 原文转载至:http://blog.csdn.net/wyc_cs/article/details/8793198 原创 2013年04月12日 14:33:35 1704 ...

随机推荐

  1. Python框架之Tornado(三)请求

    概述 本篇就来详细介绍tornado服务器(socket服务端)是如何接收用户请求数据以及如果根据用户请求的URL处理并返回数据,也就是上图的3 系列所有步骤,如上图[start]是一个死循环,其中利 ...

  2. js学习笔记之标准库

    在全局函数中,this等于window  在函数被作为某个对象的方法调用时,this等于那个对象. 数组的函数: 检测:Array.isArray() 转换:toString(),toLocalStr ...

  3. WPF整理-二进制资源和内容

    WPF中的Binary Resource(二进制资源)是相对于前面所说的Logical resource(逻辑资源)而说的,一般指Image.XML文件等. 注意:这里说的是Resource" ...

  4. 利用js来实现文字的滚动(也就是我们常常见到的新闻版块中的公示公告)

    首先先看一下大致效果图(因为是动态的,在页面无法显示出来) 具体的实现代码如下: 1.首先是css代码: <style type="text/css"> body,ul ...

  5. TestNG Assert 详解

    org.testng.Assert 用来校验接口测试的结果,那么它提供哪些方法呢? 中心为Assert测试类,一级节点为方法例如assertEquals,二级结点为参数类型及参数个数,double 3 ...

  6. Java中有关Null的9件事

    对于Java程序员来说,null是令人头痛的东西.时常会受到空指针异常 (NPE)的骚扰.连Java的发明者都承认这是他的一项巨大失误.Java为什么要保留null呢?null出现有一段时间了,并且我 ...

  7. HTML学习有感

    自从到大三之后一直在纠结课下去学些什么,刚开始一直在学PS,当时学的还算可以,可以一段时间不用之后就忘记了,这使我很郁闷!之后一直想学JAVA,跟已经工作的同学讨来了相关的视屏资料以及他培训时的笔记: ...

  8. NEsper Nuget包

    Esper是专门进行复杂事件处理(CEP)的流处理平台,Java版本为Esper,.Net版本为NEsper.Esper & NEsper可以方便开发者快速开发部署处理大容量消息和事件的应用系 ...

  9. Metrics.NET 项目

    Metrics.NET(https://github.com/etishor/Metrics.NET)是一个给CLR 提供度量工具的包,它是移植自Java的metrics,在c#代码中嵌入Metric ...

  10. 旺信UWP正式版发布

    下载链接:https://www.microsoft.com/store/apps/9nblggh5lq9x 各位园主好,在旺信Beta版发布后近两个月,我们的新版本1.1.0终于上线了,并且更名为旺 ...