C/C++中,空数组、空类、类中空数组的解析及其作用
转自:http://blog.sina.com.cn/s/blog_93b45b0f01015s95.html
我们经常会遇到这些问题:
(1)C++中定义一个空类,他们它的大小(sizeof) 为多少?
(2)只有一个char数据成员的类的大小?
(3)能否定义一个空数组?
(4)空数组名做标示的指针指向什么地方?
(5)空类有什么用?
(6)空数组有什么用?
等等......
这些问题,笔者在这篇文章统统做一个比较详细的解析和认识。
1. sizeof是什么?
首先我们要理解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/C++语言为空类,以及相关的空类对象设计一套特殊的规则,是这个语言变得非常不合理)。
所以,C++的选择是,自动的给空类插入一个char类型(只一个特殊对待),只要这个类对象将来占空间,那么就可以通过地址来区分他们。
3. 空类的作用
还有一个关于空类的疑问就是,C++语言有必要保留空类吗?空类实现空对象有什么用?
有用的,尤其是在“泛型编程”中,空类(结构)的用处非常广:
在其他的文章中提到,我们利用类型(通常是空类)来区别对待不同类对象的属性。(其实我们是可以通过使用常数来区分的,但是区别我们很容易就能知道)。
使用常数来区分需要使用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];
使用sizeof的时候你c猜是多少:
0
好吧,这里0,我们可以理解。
但是问题就来了:
既然前面对于空类的情况中,因为需要让对象唯一定位,所以插入char,那么空数组既然sizeof的大小为0,那应该就是不占空间,那么如何区分。
事实上,对于空数组,在C/C++有着特别的交代(可能是具体的实现不同,这里只是使用GCC,G++)。
空数组名是一个指针,(但是又不占空间)指向一个位置:
对于结构体中,空数组名这个指针指向了前面一个成员结束的第一个空间。
对于非结构题中,空数组名这个指针指向的内容,与前一个对象的指针的内容一样(虽然可能他们的类型不一样)。
虽然,我们不知道这两种安排有什么玄机,或者益处,但是无所谓(下一节会介绍空数组的作用),但是针对空数组的sizeof为什么可以为0,我们有了解释。
与空类,不同的是,空数组是一个对象,而不是一个类。既然我们这个对象定义出来了,而且它会指向一个空间(虽然这个空间可能会与其他的地方重叠)但是,我们总算能够区分开不同的空数组。
5. 空数组的作用
其实在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的空间。
这样做还有一个好处,如果我们分开管理结构体和缓冲区(通常这个时候结构体是一个小碎片)。在申请和释放的时候,很容易在内存中制造出碎片。
而如果向上面的这样管理,结构体,依附这缓冲区(大块)一起被管理。那将是一个很美好的事情。这就是空数组的用处。
6. 类中空数组
class T
{
int a[0];
};
您看这个类的sizeof为多少:
0
你会觉得这像个玩笑,但是其实仔细分析一下,也是可以所通的。
我前面说给一个空类插入一个char成员,是因为想让程序能够区分该类的不同对象。
而这里,我们知道由于空数组是不占内存的,它就像一个指针指向某个地方,但是又不占内存。但是的确我们的类对象能够借助空数组这个东西来区分开来。
所以既然目的达到了,那么为什么还要加入什么一些什么信息呢?
C/C++中,空数组、空类、类中空数组的解析及其作用的更多相关文章
- 转:C/C++中,空数组、空类、类中空数组的解析及其作用
转自:http://blog.sina.com.cn/s/blog_93b45b0f01015s95.html 我们经常会遇到这些问题: (1)C++中定义一个空类,他们它的大小(sizeof) 为多 ...
- 观V8源码中的array.js,解析 Array.prototype.slice为什么能将类数组对象转为真正的数组?
在官方的解释中,如[mdn] The slice() method returns a shallow copy of a portion of an array into a new array o ...
- Java中字符数组、String类、StringBuffer三者的相互转换
一.StringBuffer与String的相互转换 1.将StringBuffer转换成String StringBuffer类成员toString函数可将其转换成String类型. StringB ...
- java中 引用传递、值传递的理解(数组,自定义类,基本数据类型,String类)
代码部分: public static void main(String[] args) { testInt(); testString(); testArray(); testX(); } publ ...
- C++中若类中没有默认构造函数,如何使用对象数组
前言: 如果定义一个类,有其默认的构造函数,则使用new动态实例化一个对象数组,不是件难事,如下代码: #include <memory> #include <iostream> ...
- C#中的结构体与类的区别
经常听到有朋友在讨论C#中的结构与类有什么区别.正好这几日闲来无事,自己总结一下,希望大家指点. 1. 首先是语法定义上的区别啦,这个就不用多说了.定义类使用关键字class 定义结构使用关键字str ...
- Java基础学习-Random类和Java数组
1.随机数类(Random) package com.denniscui; import java.util.Random; /* * Random:用于产生随机数 * * 使用步骤: * ...
- C++的IO处理中的头文件以及类理解(2)<sstream>头文件
C++的IO处理中的头文件以及类理解(2)<sstream>头文件 头文件<sstream>中定义的类型都继承iostream头文件中定义的类型.除了继承得来的操作,sstre ...
- JAVA中的集合容器操作类
目录 JAVA中的集合容器操作类 List集合 ArrayList的操作方法说明 LinkedList Stack Set Map Queue 总结 JAVA中的集合容器操作类 Java容器类库总共分 ...
随机推荐
- 去掉Chrome手机版首屏的“推荐的文章”
百度可得很多类似的文章,然而都是失效的,,比如此文,本文演示所使用的Chrome版本为59. 百度所得的解决办法都是同一个,排版,截图都是一样的,害我浪费了不少力气. 第一,转载文章未标明文章出处: ...
- []ARC098
咕咕咕 C:普及组难度的题 D:给定$a_{1\cdots n}$,求有多少$1\leq l\leq r\leq n$满足$x_l+\cdots+x_r=x_l\text^\cdots\text^x_ ...
- 【枚举】【贪心】 Codeforces Round #398 (Div. 2) B. The Queue
卡题意……妈的智障 一个人的服务时间完整包含在整个工作时间以内. 显然,如果有空档的时间,并且能再下班之前完结,那么直接输出即可,显然取最左侧的空档最优. 如果没有的话,就要考虑“挤掉”某个人,就是在 ...
- ListView控件(上)数据适配器:ListView绑定监听是SetOnItemClickListener
(一) 1.效果图: 2.MainActivity.java package com.example.app5; import android.support.v7.app.AppCompatActi ...
- web 中加载配置文件
1.web.xml中配置 <!-- 加载配置文件 --> <listener> <description>ServletContextListen ...
- 我是如何给discuz模板做语法高亮的/vs code/textmate
本人一直做ASP.NET开发,近期接到任务要用Discuz开发一个社区,第一次接触PHP,PHP灵活的语法,天生的模块化,各种语法糖深深的震惊了我,我从内心深处感受到了PHP是最牛逼的语言!!! 好了 ...
- 使用GIT进行源码管理 —— VisualStudio官方GIT教程
我之前在文章使用GIT进行源码管理 —— 在VisualStudio中使用GIT中简单的介绍了一下如何使用VS中自带的Git工具,今天发现MSDN上现在也有了非常完整的教程,感兴趣的朋友可以看一下: ...
- 【java】获取解析资源文件的方法
关于资源文件的读取,有很多种方法,下面补充了多种方法 1.java.util.ResourceBundle 使用java自带的util包下的ResourceBundle类获取,使用方法最简单 //获取 ...
- ylbtech-LanguageSamples-Attibutes(特性)
ylbtech-Microsoft-CSharpSamples:ylbtech-LanguageSamples-Attibutes(特性) 1.A,示例(Sample) 返回顶部 “特性”示例 本示例 ...
- linux sudo使用学习记录
sudo在linux中非常重要,它能够使普通的用户临时拥有root权限.但是如果让用户滥用sudo命令的话可能会造成严重的影响. 例如:修改root的密码,切换到root用户等等. 所以我们虽然需要赋 ...