面试题-面向对象篇

面向对象问题汇总

类的大小

Q:一个只含有虚函数的类的size为多少?
A:参考这个博客
#类中的元素
0. 成员变量 1. 成员函数 2. 静态成员变量 3. 静态成员函数 4. 虚函数 5. 纯虚函数
#影响对象大小的因素
0. 成员变量 1. 虚函数表指针(_vftptr) 2. 虚基类表指针(_vbtptr) 3. 内存对齐
**什么都没有,占用一个字节**sizeof(空类的实例) = 1 byte ,如果有其他的,这个字节就不用算了。
类函数不占用空间,加了构造函数和析构函数,sizeof(实例) = 1byte
有虚函数,实例中需要添加一个虚函数表的指针 sizeof(实例) = 4 byte
数据成员占用空间 int4 char1 注意要对齐

Q:含虚函数类的虚函数表是存放在哪里的
C++虚函数的具体实现原理(虚函数和虚函数表是热门问题,一定要弄清楚其原理与实现)
实现编译器处理虚函数表应该如何处理
A:
虚函数表vtbl实际上是一个指针数组,存放了虚函数的地址。指向虚函数表的指针vptr存在对象实例的最前面的位置。
虚函数指针是在构造函数执行时初始化的,虚函数表存放在了目标文件或者可执行文件的常量段中。虚函数表vtable在Linux/Unix中存放在可执行文件的只读数据段中(rodata)
虚表在静态区,因为对每个类,所有对象共用虚表。

Q:C++的构造函数可以为虚函数吗?析构函数一般写成虚函数的原因
A:
构造函数不能为虚函数,而析构函数可以且常常是虚函数。
C++对象的构造,C++对象在三个地方构建:(1)函数堆栈;(2)*存储区,或称之为堆;(3)静态存储区。
无论在那里构建,过程都是两步:首先,分配一块内存;其次,调用构造函数。
虚函数需要通过vtable来调用,而vtable是在构造函数中初始化的,所以构造函数不能为虚函数。
析构函数需要写成虚函数
防止只析构基类而不析构派生类的状况发生,想析构子类中的重新定义或新的成员及对象,当然也应该声明为虚的

Q:什么情况下要给类写拷贝构造函数
A:
三种情况下会调用拷贝构造函数:
1) 一个对象作为函数参数,以值传递的方式传入函数体;
2) 一个对象作为函数返回值,以值传递的方式从函数返回;
3) 一个对象用于给另外一个对象进行初始化(常称为赋值初始化);
需要定义拷贝构造函数
如果你需要定义一个非空的析构函数,那么,通常情况下你也需要定义一个拷贝构造函数。
①对于凡是包含动态分配成员或包含指针成员的类都应该提供拷贝构造函数;②在提供拷贝构造函数的同时,还应该考虑重载”=”赋值操作符号。

Q:为什么用成员初始化列表会快一些
A:
foo(string s, int i):name(s), id(i){}
省去了临时对象的存在,C++的赋值操作是会产生临时对象的。对内置类型没有多大差别。而类类型则减少了一次调用默认构造函数,直接调用拷贝构造函数。
必须用初始化列表的情况:1)类中const成员 2)其它类B作为成员,类B禁止了赋值操作

Q:struct与class的区别
A:
默认的访问控制:默认的继承访问权限(struct是public的,class是private的);默认的数据访问控制(成员变量访问控制struct是public的,class是private的)
struct更体现出一种数据结构,class更像【对象】
“class”这个关键字还用于定义模板参数,就像“typename”

Q:C++内存模型
A:
C语言:(由低地址到高地址)
a)正文段:用来存放程序执行代码。通常,正文段是可共享的。另外,正文段常常是只读的,一次防止程序由于意外修改其自身。
b)初始化数据段:用来存放程序中已初始化的全局变量。数据段属于静态内存分配。
c)非初始化数据段:通常称为BSS段, 用来存放程序中未初始化的全局变量。BSS是英文Block Started by Symbol(由符号开始的块)的简称。BSS段属于静态内存分配。 在程序开始执行之前,内核将此段中的数据初始化为0或者空指针。
d)堆:堆是用于存放进程运行中被动态分配的内存段,它的大小并不固定,可动态扩张或缩减。当进程调用malloc/free等函数分配内存时,新分配的内存就被动态添加到堆上 (堆被扩张)/释放的内存从堆中被剔除(堆被缩减)。
e)栈:栈又称堆栈, 存放程序的局部变量(但不包括static声明的变量,static意味着在数据段中存放变量)。除此以外,在函数被调用时,栈用来传递参数和返回值。由于栈 的先进后出特点,所以栈特别方便用来保存/恢复调用现场。
C++中,内存分成5个区,他们分别是*存续区全局/静态存续区常量存续区
a) 栈:内存由编译器在需要时自动分配和释放。通常用来存储局部变量和函数参数。栈运算分配内置于处理器的指令集中,效率很高,但是分配的内存容量有限。在大多数C编译器中,参数是由右往左入栈
b) 堆:内存使用new进行分配使用delete或delete[]释放。如果未能对内存进行正确的释放,会造成内存泄漏。但在程序结束时,会由操作系统自动回收。
堆是向高地址扩展数据结构,是不连续内存区域。空间比较大。系统是用链表来存储空闲内存地址,链表遍历方向是由低地址向高地址。会在这块内存空间中首地址处记录本次分配大小,这样,代码中delete语句才能正确释放本内存空间。
c) *存储区:使用malloc进行分配,使用free进行回收。和堆类似。
d) 全局/静态存储区:全局变量和静态变量被分配到同一块内存中,C语言中区分初始化和未初始化的,C++中不再区分了。(静态局部变量生存期为整个源程序,在其他函数中它是“不可见”的。)
e) 常量存储区:存储常量,不允许被修改。
Q:动态绑定
A:
多态,运行时确定地址,虚函数实现。

Q:new 和malloc的区别
1)类型不同:
malloc与free是C++/C语言的标准库函数
new/delete是C++的运算符
2)分配内存的位置不同:
malloc函数从上动态分配内存
new操作符从*存储区(free store)上为对象动态分配内存空间
*存储区不仅可以是堆,还可以是静态存储区
3)构造函数/析构函数
new/delete会调用对象的构造函数/析构函数以完成对象的构造/析构。
而malloc则不会。
参考链接:http://www.linuxidc.com/Linux/2016-01/127591.htm
面试题-面向对象篇