空结构体与空类解析

 我们知道在C++中结构体和类是差不多的,有一个明显的区别就是:结构体默认的类型是公有的,而类中的成员默认是私有的。 

但是在C语言中的结构体就与C++中的结构体有所不同了。先说一个小小的不同,C语言中定义结构体变量必须写上struct关键字,但是一个.cpp文件定义结构体变量,直接写结构体名称即可,因为我们上边说了,C++中结构体和类只有访问限定哪一个明显的区别,其他基本一样,所以结构体名称就相当于类名,所以就不必加上struct关键字。

接下来我们先来看一下C语言中一个空结构体的大小是多少?

#include<stdio.h>

struct Node
{
};

int main()
{
	struct Node m;
	printf("%d\n",sizeof(m));
	return 0;
}

同样一份代码,我们分别在Windows底下的VS和Linux下gcc编译,惊讶的发现竟然是不一样的。

由图可以看出我们在VS中并不能定义一个空的结构体,所以也并不存在它的大小是多少。接下来同样的代码在Linux中编译,结果是通过的。

所以我们运行一下结果如下:

由此可以看出来大小为0,那么我们定义了一个结构体变量他在哪里存的呢?为了验证这个,我们再加点代码一起来看看吧

#include<stdio.h>

struct Node
{
};

int main()
{
	char s;
	struct Node m;
	char n;
	printf("%x,%x,%x\n",&s,&m,&n);
	printf("%d\n",sizeof(m));
	return 0;
}

执行出来的结果是:

可以看出这个结构体变量的起始内存与s的内存一致,并没有自己独立的内存(因为它的大小是0呀。。。)

我也是感觉很惊奇的。。。具体为什么这么做,我也不太清楚。。。欢迎大家补充^ ^

下来我们看一下C++中空类的大小是如何的吧?

#include <iostream>
using namespace std;
class A
{

};
class B
{
public:
	B(){}
	~B(){}
	int find()
	{
		cout<<"hello\n"<<endl;
	}
};
class C
{
public:
	C(){}
	virtual ~C();
protected:
	int mc;    //成员属性是每一个实例对象私有的,所以有内存大小的开辟
private:
	int mm;    //能继承过去,但是不能访问
};
class D:public C
{
public:
	D(){}
	~D(){}
private:
	int md;
};
int main()
{
	cout<<sizeof(A)<<endl;    //1
	cout<<sizeof(B)<<endl;    //加了构造和析构仍是1,加上函数也仍然是1,因为成员函数只与类型有关,与实例对象无关
	cout<<sizeof(C)<<endl;    //虚函数会生成虚函数表,每个实例对象会有一个指针指向虚函数表,所以指针大小是4
	cout<<sizeof(D)<<endl;    //要继承基类的成员内存大小,私有成员属性也继承,但不能访问
	
	return 0;
}

从运行结果看是不是有点惊讶呢?空类A大小竟然不是0而是1,为什么会这样呢?这就是实例化的原因(空类同样可以被实例化),每个实例在内存中都有一个独一无二的地址,为了达到这个目的,编译器往往会给一个空类隐含的加一个字节,这样空类在实例化后在内存得到了独一无二的地址,所以空类所占的内存大小是1个字节。那么为什么类B也是1呢?我们都知道成员变量是每个对象私有的,但是成员方法是对象所共有的,即成员方法只与每一个类有关,而与类型的实例无关,所以编译器不会在实例内添加任何额外的信息。类C有一个虚析构函数,值显然不是我们所想的8而是12,这又是为什么呢?因为C中有虚函数,他就会为该类型生成虚函数表,并在该类型的每一个实例中添加一个指向虚函数表的指针,在32位的机器上,一个指针占4个字节,所以答案会是12;D类继承了C并且有自己的一个成员属性,所以类D的大小是16.

那么大家是否知道一个空类中有哪些方法?

如果你只是声明一个空类,不做任何事情的话,编译器会自动为你生成一个默认构造函数、一个默认的拷贝构造函数、一个默认的赋值运算符重载和一个默认析构函数。这些函数只有在第一次被调用时,才会别编译器创建。所有这些函数都是inline和public的。

猜你喜欢

转载自blog.csdn.net/Eunice_fan1207/article/details/81255302