理一理空类,空数组的相关问题

本文探讨了C++中空类的大小、作用,以及空数组的定义、特性与用途。空类在编译时会被自动插入一个char成员以占用空间,而在STL等泛型编程中起到关键作用。空数组则可以用于结构体中作为缓冲区的灵活管理,避免内存碎片。同时,类中的空数组也能实现对象区分,不额外占用内存。
摘要由CSDN通过智能技术生成

我们经常会遇到这些问题:

1C++中定义一个空类,他们它的大小(sizeof 为多少?

2)只有一个char数据成员的类的大小?

3)能否定义一个空数组?

4)空数组名做标示的指针指向什么地方?

5)空类有什么用?

6)空数组有什么用?

这些问题,笔者在这篇文章统统做一个解析和认识。

 

 

1sizeof是什么?

首先我们要理解sizeof是什么东西?对于C++,对象的类型的大小在编译的时候直接可以确定,所以我们要明白sizeof并不是一个函数,而是一个返回对象类型大小的宏,这个宏的参数可以是对象,也可以是类型。在编译结束后,sizeof的那个位置上面是直接被替换成一个常数的,我们用一个简单而直观的实验来证实一下:

int main()
{
        int a=2;
        int b[sizeof(a)];
        cout<<sizeof(b)/sizeof(int)<<endl;
}


如果 sizeof 是作为的函数,那么这个是不可能编译成功的,因为栈上定义数组是一定需要常数(或常数表达式的)。这样我们就能知道:

sizeof(i++);这样一句话,i是不可能加1的,因为这句话在编译的时候就已经被转换成的对应的常数,这是常常出现的陷阱之一。

 

2,空类的大小

一个空类(或则是空类对象)使用sizeof的时候,结果是1。如果真是空类,那么对于同一个空类的对象就不会占空间,不会占空间就意味着无法区分。所以,C++的选择是,自动的给空类插入一个char类型(只一个特殊对待),只要这个类对象将来占空间,那么就可以通过地址来区分他们。

 

3,空类的作用

有用的,尤其是在泛型编程中,空类(结构)的用处非常广:

我们利用类型(通常是空类)来区别对待不同类对象的属性,其实我们是可以通过使用常数来区分的,但是区别:

使用常数来区分需要使用if else的这种运行时来确定执行的线路的方法,而使用函数重载的方法,在参数中加入一个空类域作为区分不同的函数的方法,编译的时候直接选择,而不是在运行的时候选择,这是非常提高效率的

STL中,使用空类区分不同类型的标志,从而在编译的时候来对不同的类进行有针对性的优化是非常常见的。


template <typename A>
void fun(A a)
{
   typedef typename trait<A>::type T;
   _fun(A a, *(new T()));
}  
template <typename A>
void _fun(A a, int)
{
   ……
}
template <typename A>
void _fun(A b, float)
{
   ……
}
 

当然,空类应该还有其他的用处。空类是C++中一个有用的机制,不同名称的空类代表着不同的类型空类在编译的时候会被编译器自动的加入一个char成员,不为别的,只是为了,让它被实例后的对象占有空间,从而可以区分。

 

4,空数组

C中我们可以定义空数组:

int a[0];
len = sizeof(a[0]) ;

这里len = 0,我们可以理解。但是问题就来了:

空数组名是一个指针,(但是又不占空间)指向一个位置

对于结构体中,空数组名这个指针指向了前面一个成员结束的第一个空间。

对于非结构体中,空数组名这个指针指向的内容,与前一个对象的指针的内容一样(虽然可能他们的类型不一样)(有待考究)

与空类,不同的是,空数组是一个对象,而不是一个类。既然我们这个对象定义出来了,而且它会指向一个空间(虽然这个空间可能会与其他的地方重叠)但是,我们总算能够区分开不同的空数组。

 

6,空数组的作用

其实在C里面,空数组的使用是非常多的。

问题:

假如你想要给一个结构体,添加一个缓冲区。你会怎么做?


1)定义一个固定长度的buffer数组成员,这样的不好之处在于buffer会被定死

2)定义一个buffer指针,在构造函数(虽然C没有,但可以使用initialize函数来代替)中动态的创建一个需要大小的buffer,给这个结构体使用。但是这样就需要我们特别管理这个空间(使用析构函数),否则会很容易出现内存泄露。并且,一个buffer指针还占有了一个空间

那么C里面,就有一个巧用空数组来达到这个问题的方法。


sttuct  T
{
   int a;
   int b;
   ……
   char buffer[];
};

这里buffer并不占空间,所以,T的对象的总大小是不会把buffer算上的

 struct T * p=(T*)malloc(sizeof(T) +buffer_len);

在声请空间的时候,加上buffer_len(所需缓冲区长度),这样结构体和缓冲区一起被申请了(在结束的时候,也可以直接使用free一起释放,可以避免独立的管理结构体和缓冲区)。buffer这个指针指向的就是结构体后面的那个buffer_len的空间。

这样做还有一个好处,如果我们分开管理结构体和缓冲区(通常这个时候结构体是一个小碎片)。在申请和释放的时候,很容易在内存中制造出碎片。而如果向上面的这样管理,结构体,依附这缓冲区(大块)一起被管理

这就是空数组的用处。

 

7,类中空数组


class T
{
  int a[0];
};

这个类的 sizeof 为多少:

0

你会觉得这像个玩笑。但是其实仔细分析一下,也是可以讲通的。我前面说给一个空类插入一个char成员,是因为想让程序能够区分该类的不同对象。而这里,我们知道由于空数组是不占内存的,它就像一个指针指向某个地方,但是又不占内存。但是的确我们的类对象能够借助空数组这个东西来区分开来。所以既然目的达到了,那么为什么还要加入什么一些什么信息呢?

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值