我们知道在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的。