前言
最近在投简历参加面试,其中遇到了一些新的问题,搜索学习之后颇有所得,便想记录下心得。
这是乐游的笔试中出现的问题。
类的大小
首先来看一段代码
a和b是空类;
c继承a类,并且有虚函数;
d继承b和c类;
#include<iostream>
using namespace std;
class a{};
class b{};
class c :public a{
virtual void fun() = 0;
};
class d :public b, public c{};
int main()
{
cout << "sizeof(a)" << sizeof(a) << endl;
cout << "sizeof(b)" << sizeof(b) << endl;
cout << "sizeof(c)" << sizeof(c) << endl;
cout << "sizeof(d)" << sizeof(d) << endl;
return 0;
}
程序的结果是:
sizeof(a)=1
sizeof(b)=1
sizeof(c)=4
sizeof(d)=8
如何解释这个现象呢?
对于a和b这两个空类,本来应该是占0字节,但是输出为1。因为类是可实例化的(即使是空类),所以一个实例需要一个内存地址,如果空类占0字节,则在内存中不能够区分该实例,所以有一个隐含的字节占位。
对于c类,如果没有虚函数,它应该也是占1字节,但是有虚函数的情况下,不需要这个隐含的字节占位,并且有虚函数的类都有一个隐藏的虚函数指针,指向第一个虚函数的地址,这个指针占4字节,所以c占4字节。(函数都存放在代码区)
对于d类,它继承了b和c类,一个占1字节,一个占4字节,加起来却不是5字节,因为内存中数据对齐的原因,将1字节的b类对齐成了4字节,当然有3个字节是空的,这也形成了某种意义上的内存中的内部碎片(一般是指页式内存管理中的页不满的情况)。
空对象指针调用成员函数
先来看一段代码
#include<iostream>
#include<string.h>
using namespace std;
class A
{
public:
static void f1(){ cout<<"f1"<<endl; }
void f2() { cout<<"f2"<<endl; }
void f3() { cout<<num<<endl; }
virtual void f4() {cout<<"f4"<<endl; }
public:
int num;
};
int main()
{
A* pa = NULL;
pa->f1(); //正常
pa->f2(); //正常
pa->f3(); //报错
pa->f4(); //报错
return 0;
程序的结果是:
f1正常;
f2正常;
f3报错;
f4报错;
如何解释这个现象呢?
首先要了解c++中成员函数是怎么存储的。其实和普通函数一样,都是存放在代码区。在物理上两者并没有什么区别,只是在逻辑上成员函数是属于类的。
从这个角度来说,f1和f2能够正常运行便能够理解。
对于f3来说,它与f2的区别是调用了成员变量,由于此时只是并没有new一个对象出来,所以当调用成员变量时,编译器会自动调用this指针来找到当前对象的变量,而现在this未分配,所以会报错。
对于f4,是因为调用虚函数会先根据虚函数指针找到虚函数的地址再调用,而此时虚函数指针未分配,所以会报错。