c++面试常用知识(sizeof计算类的大小,虚拟继承,重载,隐藏,覆盖)
一. 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计算类的大小,虚拟继承,重载,隐藏,覆盖)的更多相关文章
- sizeof求类的大小
用sizeof求类的大小,http://blog.csdn.net/szchtx/article/details/10254007(sizeof浅析(三)——求类的大小),这篇博文给出了非常详尽的举例 ...
- MySQL JDBC常用知识,封装工具类,时区问题配置,SQL注入问题
JDBC JDBC介绍 Sun公司为了简化开发人员的(对数据库的统一)操作,提供了(Java操作数据库的)规范,俗称JDBC,这些规范的由具体由具体的厂商去做 对于开发人员来说,我们只需要掌握JDBC ...
- C++ 类的继承、虚拟继承、隐藏、占用空间
主函数: #include <iostream> #include "test.h" #include "testfuc.h" using name ...
- c/c++面试12-18------关与sizeof那些事儿
12 使用sizeof计算普通变量所占空间大小 (1)不同数据类型所占字节数不同(32位 64位系统不同) int----->4 double----->8 char-------> ...
- Java && Python 算法面试常用类以及方法总结
数据结构 逻辑结构上: 包括集合,线性结构,非线性结构. 存储结构: 顺序存储,链式存储,索引存储,散列存储. Java 常见数据结构 大专栏 Java && Python 算法面试 ...
- Unity游戏开发面试基础知识
面试第一次知识总结: 一.Unity基本操作 1.unity提供哪几种光源? 点光源.平行光.聚光灯.区域光. 2.物体发生碰撞的必要条件什么? 两个物体必须有碰撞体Collider组件,一个物体上必 ...
- Java面试必备知识
JAVA面试必备知识 第一,谈谈final, finally, finalize的区别. 第二,Anonymous Inner Class (匿名内部类) 是否可以extends(继承)其它类,是否可 ...
- javascript常用知识点集
javascript常用知识点集 目录结构 一.jquery源码中常见知识点 二.javascript中原型链常见的知识点 三.常用的方法集知识点 一.jquery源码中常见的知识点 1.string ...
- Java常用的加密解密类(对称加密类)
Java常用的加密解密类 原文转载至:http://blog.csdn.net/wyc_cs/article/details/8793198 原创 2013年04月12日 14:33:35 1704 ...
随机推荐
- 介绍两个挺好用的基于Jquery的上传工具
一.ajaxFileUpload 二.fineUploader
- 让/etc/profile文件修改后立即生效
方法1: 让/etc/profile文件修改后立即生效 ,可以使用如下命令: # . /etc/profile 注意: . 和 /etc/profile 有空格 方法2: 让/etc/profile ...
- MLlib决策树与集成树
决策树是一种常见的分类与回归机器学习算法,由于其模型表达性好,便于理解,并能取得较好的效果,而受到广泛的应用.下图是一个简单的决策树,决策树每个非叶子节点包含一个条件,对于具有连续值的特征,该条件为一 ...
- Smart3D系列教程2之 《为什么三维重建效果这么差?——探探那些被忽略的拍照要求和技巧》
一.照片采集的实用概念 根据照片进行三维重建的过程中,有人没怎么遇到坑,有人被坑的不轻.可能是模型的纹理失真,模型的法线错了,模型会生成我们各种也想不到的结果,那么,是什么导致三维重建效果这么差的?是 ...
- Task异步编程
Task异步编程中,可以实现在等待耗时任务的同时,执行不依赖于该耗时任务结果的其他同步任务,提高效率. 1.Task异步编程方法签名及返回值: a) 签名有async 修饰符 b) 方法名以 Asyn ...
- ZK 样式使用
控件: <textbox id="usernameTb" sclass="login-user-input" placeholder="账号&q ...
- ES6(四) --- 正则 Number Math
想学vue了 重启ES6的学习之路 在ES5 中正则的构造器 RegExp 不支持第二个参数 ES6 做了调整 第二个参数表示正则表达式的修饰符(flag) var regex = new ...
- ADO.NET Entity Framework CodeFirst 如何输出日志(EF 5.0)
ADO.NET Entity Framework CodeFirst 如何输出日志(EF4.3) 用的EFProviderWrappers ,这个组件好久没有更新了,对于SQL执行日志的解决方案的需求 ...
- 借助 Lucene.Net 构建站内搜索引擎(上)
前言:最近翻开了之前老杨(杨中科)的Lucene.Net站内搜索项目的教学视频,于是作为老杨脑残粉的我又跟着复习了一遍,学习途中做了一些笔记也就成了接下来您看到的这篇博文,仅仅是我的个人笔记,大神请呵 ...
- 大白话讲解Promise(三)搞懂jquery中的Promise
前两篇我们讲了ES6中的Promise以及Promise/A+规范,在Promise的知识体系中,jquery当然是必不可少的一环,所以本篇就来讲讲jquery中的Promise,也就是我们所知道的D ...