C++编程思想,在介绍数据封装给了一个CStash的例子,大概的思想是,建立一个struct,来管理一个动态增长的数组。这个数组可以接受任何类型的基本数据类型。包括示例中的int和char,一个能保存多种数据类型的底层数据类型,当然是最小的类型也就是sizeof运算符返回为1的数据类型,综合考虑,原书中给出的unsigned char作为最基本的数据类型。实际上对应于标准的字节数组。对于这个转化过程,我写了一些代码验证了一些问题。顺便复习一下,计算机体系结构中的字节序的问题。
以下是我的CStash实现代码:
#include<iostream>
#include<cstdlib>
using namespace std;
const int increment = 100;
struct CStash
{
int size;//当前元素所占字节数
int quantity;//当前容积(即包含未占用的和已占用的)
int next;//当前元素个数(因为坐标从0开始,所以next实际上等于当前元素个数,又是下一个可以存放的空白位置)
unsigned char *buffer;//字节数组
//初始化
void init(int sz)
{
size = sz;
quantity = 0;
next = 0;
buffer = 0;
cout << "Init......over!" <<endl;
}
//释放内存
void cleanup()
{
if(buffer != 0)
delete []buffer;
cout << "Cleanup......over!" <<endl;
}
//添加元素
int add(const void * element)
{
//当前元素数量超过容积,则动态增长
if(next >= quantity)
flate(increment);
//转换为字节数组,因为指针的关系,首地址是不变的,数据内容并不会改变
unsigned char *cur = (unsigned char *)element;
//当前字节数(元素个数 * 每个元素的字节数)
int startbytes = next * size;//从starybytes开始 都可以存放新的数据
for(int i=0;i<size;i++)//因为一个元素是size个字节,复制size个字节到新空间
{
buffer[startbytes + i] = cur[i];
}
next++;//元素个数加1
return next - 1;//返回当前最后一个元素,其实按照标准模版库的一般用法给next返回,也没啥问题
}
//获取当前位置的原书
void* fetch(int index)
{
if(index >= next) return 0;
//用引用返回,而不是局部指针
return &(buffer[index * size]);
}
int count()
{
//当前元素个数
return next;
}
void flate(int incre)
{
int newQuantity = quantity + incre;//新增长后元素数量
int newSize = newQuantity * size;//新容器整个字节数
int oldSize = quantity * size;//旧容器中字节数
unsigned char *tmp = new unsigned char[newSize];//重新分配内存
//复制旧元素
for(int i=0;i<oldSize;i++)
{
tmp[i] = buffer[i];
}
delete []buffer;//释放旧元素内存
buffer = tmp;//赋值新元素地址
quantity = newQuantity;//修改容器容量
}
};
int main()
{
//验证unsigned char作为字节数组的可行性,刚好就是1字节
cout << sizeof(unsigned char) <<endl;
CStash s;
//内存初始化
s.init(sizeof(int));
for(int i=0;i<100;i++)
{
s.add(&i);//添加元素
}
for(int i=0;i<s.count();i++)
{
cout << i << " : "<<*((int *)s.fetch(i))<<endl;//输出元素
}
cout << endl;
//内存释放区域
s.cleanup();
//暂停
system("pause");
}
现在分析上面代码值得学习的地方,首先为什么是无符号的字符数组,因为有符号的字节数组,因为符号的原因,会产生非预期的效果。其次添加元素和查找元素参数都是指针,指针变化的只是指针能访问数据类型的限制,而不是数据本身,所以这种转换并没有破坏数据本身。而且void * 使得程序能处理更通用的数据类型。还有这个动态增长的思想,其实标准库中有很多地方都有这种思想存在,这里的代码给人更直观的学习机会!
为了验证一个int(我的机器上32位环境下4字节)转换为unsigned char过程中发生了什么,这就涉及到了一点计算机体系结构的知识,也就是字节序的问题了。
验证程序如下:
#include<iostream>
#include<cstdlib>
#include<cstdio>
using namespace std;
void Convert(void *element,int size)
{
//转换为字节数组指针
unsigned char *tmp = (unsigned char *)element;
for(int i=0;i<size;i++)
{
printf("%d ",(int)tmp[i]);//输出每个字节的内容
}
printf("\n");
}
int main()
{
int a = 10;//可以设置10 1024等验证结论
Convert(&a,sizeof(a));
system("pause");
return 0;
}
上面的程序将int * 转化为 unsigned char *之后查看四个字节的内容,得到了非常有意思的结果
10-----10 0 0 0
1024--0 4 0 0
看到没,0 4 0 0和10 0 0 0都说明是从低位开始放置数据的,低位数据是放在小地址上面的。按照维基百科(关键字:字节序)的说法,X86体系的计算机是小端序的,也就是从低字节开始增长。 为什么是0 4 0 0应为 2^10 = 1024 (二进制100 0000 0000) 十进制为0 0 4 0 反过来就是 0 4 0 0。小端序就是说地址是从低位开始增长,而且数据的低位放在低地址,高位放在高地址。