返回信息流动态分配数组估计是最常用的操作之一了,但是动态分配连续空间的任意维度数组(容器套容器那个不算数组,也不连续),我在网上搜了半天,没发现合适的code。所以咬咬牙自己写了个,基本算根治此问题了吧。。。以后就调用这个动态分配数组了
code不handle异常(本来写了,后来发现贴出来看上去太恶心了。。),所有异常直接抛到调用点,自行处理下即可。
先上code,然后慢慢敲解释和逻辑,可能有点绕。。。
主要绕在模板类的递归继承和儿子函数对父亲的递归调用。。。但不用这个手段的话,无法解决任意维的问题。。
template<class ElemClass, size_t N_Dim>
class DynArrBuilder : public DynArrBuilder<ElemClass*, N_Dim-1> {
protected:
static void* _bldCurDim(size_t argc, size_t args[], ElemClass lower_dim) {
size_t dim_size = 1;
for (size_t i = 0; i < argc; ++i)
dim_size *= args[i];
ElemClass* higher_dim = new ElemClass[dim_size];
higher_dim[0] = lower_dim;
for (size_t i = 1; i < dim_size; ++i)
higher_dim[i] = higher_dim[i - 1] + args[argc];
return DynArrBuilder <ElemClass*, N_Dim - 1>::_bldCurDim(argc - 1, args, higher_dim);
}
static ElemClass _freeCurDim(void * ptr){
ElemClass * cur_ptr = DynArrBuilder <ElemClass*, N_Dim - 1>::_freeCurDim(ptr);
if (cur_ptr == NULL) return NULL;
ElemClass lower_ptr = *cur_ptr;
delete [] (cur_ptr);
return lower_ptr;
}
public:
static void * arrallocate(size_t args[]){
size_t dim_size = 1;
for (size_t i = 0; i < N_Dim; ++i)
dim_size *= args[i];
ElemClass* base = reinterpret_cast <ElemClass*> (operator new (dim_size*sizeof(ElemClass)));
return DynArrBuilder <ElemClass*, N_Dim - 1>::_bldCurDim(N_Dim - 1, args, base);
}
static void * arrnew(size_t args[]) {
size_t dim_size = 1;
for (size_t i = 0; i < N_Dim; ++i)
dim_size *= args[i];
ElemClass* base = new ElemClass[dim_size];
return DynArrBuilder <ElemClass*, N_Dim - 1>::_bldCurDim(N_Dim - 1, args, base);
}
static int arrfree(void * ptr) {
if (ptr == NULL) return CODE0;
ElemClass * cur_ptr = DynArrBuilder <ElemClass*, N_Dim - 1>::_freeCurDim(ptr);
if (cur_ptr == NULL) return CODE2;
operator delete (cur_ptr);
return CODE1
}
static int arrdelete(void * ptr) {
if (ptr == NULL) return CODE0;
ElemClass * cur_ptr = DynArrBuilder <ElemClass*, N_Dim - 1>::_freeCurDim(ptr);
if (cur_ptr == NULL) return CODE2;
delete [] cur_ptr;
return CODE1;
}
};
template <class ElemClass>
class DynArrBuilder< ElemClass, 0> {
protected:
static void * _bldCurDim (size_t argc, size_t args[], ElemClass lower_base) {
return lower_base;
}
static ElemClass _freeCurDim(void * ptr){
return static_cast<ElemClass>(ptr);
}
};
这是一条镜像帖。来源:北邮人论坛 / cpp / #49156同步于 2011/2/8
该镜像源已超过 30 天没有更新,可能在源站已被删除。
CPP机器人发帖
【真绕..】share个用来动态分配 连续空间 任意维数组(非类模拟
fentoyal
2011/2/8镜像同步2 回复
订阅后,新回复会通过你的通知中心匿名送达。
2 条回复
功能和用法:
size_t args[] = {2,3,3,3};//首先定一个unsigned int型数组,来存放各维度的大小
AAAA **** omg = (AAAA****)DynArrBuilder<AAAA, 4>::arrallocate(args) ;//挺直观的吧?
DynArrBuilder<AAAA, 4>::arrfree(omg);//释放空间
这里还提供了另外一套 arrnew和arrdelete; 两套用法完全一样,区别是:arrnew做的是类似new的活(比如会调用默认构造函数啊),arrallocate只分配合适大小的空间,不干别的。然后用对应的释放函数释放即可
AAAA是个类,然后用如此格式声明数组存放的类型(AAAA)和维度(4)及各维度大小(args),返回值就是一个具有同样维度的指针。注意这里要类型转换下返回值,因为返回值本来是void *。为了不同维度之间的传递,这里我只好用void*
然后对omg的操作就类似操作平时数组,因为omg本质就跟多维数组一模一样:
比如omg[1]就是一个AAAA***的数组
omg[1][2]就是一个AAAA**的数组
最后,omg[1][2][2][2]就是一个AAA类型的东西
由于空间是任意维连续的,遍历omg可以简单的:
for (size_t i = 0; i < 2*3*3*3; ++i)
cout<<(***omg)[i].val<<endl;
以上遍历完全等价于:
for (size_t i = 0; i < 2; ++i)
for (size_t j = 0; j < 3; ++j)
for (size_t k = 0; k < 3; ++k)
for (size_t l = 0; l < 3; ++l)
cout<<omg[i][j][k][l].val<<endl;
这就是连续的好处,易管理存取
优点:
网上常见的各类数组类,有
1. 用类模拟数组,Array<Array<int>>来模拟一个二维数组。
这个首先不是连续空间。高维的话得手动累code了。而且这是用类,并不是真正的基础类型--数组,个人偏好能操作基础类型,就不用integrated的
2. 用一维数组代替整个高维,存取直接做下标运算,这是我以前常用的方法。这个数组时连续空间,但不支持[][]操作
3。 用int ** array = new int*[size];这样array就是个数组的数组(二维数组),再循环分配array[i]中每一个一维数组空间。
这个可以支持很好的[][]存取操作,但它的空间不是连续的。比如对于这样分配的一个大小为 2*3的数组,我们不能用
for (int i = 0; i < 2*3; ++i )
*array[i] =xxx;
的方法遍历数组,因为它每一维之间不连续
4. 就是我选用的方法,就是先建一个整个的连续空间,再一维一维往上建低级的索引,最后就是达到了一个连续数组空间的目的
但网上只有二维的code,对于高维的,甚至任意维的数组,没有。由于我做图像需要用到3 4维的数组,所以写了这个可以handle 任意维的类
逻辑:
本来是写了注释的,但贴出来太乱,容易让人想吐,所以没贴,另行发帖解释
首先调用arrallocate时,函数直接分配需要的所有一维空间,比如需求是int array[2][3][4];就直接分配2*3*4=24的空间
比如这里假设是p指向此空间
然后在这一维基础上,建立上级索引就是再建一个数组q,这个数组是二维的,大小2*3=6,他的每一维是个int*;然后将它对应到p上,p中每4个int算一个q[i]的
即q[0] = p之后,q[i] = q[i-1]+4;
然后以此类推建到最顶层。这样空间是连续的,也支持[][][]的操作,每级[]也确实是实实在在的下级指针。
但实现任意维的话,有点困难,所以代码很绕,前后尝试了很多方法,折腾了很久
首先要解决,如何一级指针一级指针往上走,就是类型操作。所以用了模板的递归特例化,这样可以一直将*号加到想要的数量int ****此处省略一万个星****也无妨,写个数字即可。
本来用的是函数模板,然后悲催的是,折腾了半天,发现函数模板是不支持partial instantiate(部分特例化?)的,所以用了类模板,然后用静态函数。
递归起来的逻辑就是,建好一层,当前类型上加个*,然后传给上一层,继续建。每向上一层,指针多一个维度。直到建到最高层,返回。其中返回值都是void*。
否则就是最高级返回 int ****然后返回int***,然后int**, int*....这么没法连起来,所以用的void*
delete逻辑相反,先递归调用,直奔最高层,delete了int ****返回低层,delete int***再返回,直到最底层。
细节的话,参见code
缺陷:
不够robust。
分配时倒没啥问题,就是得用户自己写异常处理,或者加到类里也好,不过那样看着很繁琐,我又删了。由于都是递归,如果有异常早晚都会抛到调用点,集中处理即可。
AAAA **** omg = NULL;
try{
omg = (AAAA****)DynArrBuilder<AAAA, 4>::arrallocate(args) ;
}
catch(const std::bad_alloc &ex)
{
cout<<ex.what()<<endl;
exit(-2);
}
delete时可能有问题,务必要确保那个被delete的指针是用这里提供的对应分配函数分配的,否则,delete会出问题。但我不知道怎么做这个判断。。。所以空没管。
最后
那个arrallocate函数,是只分配空间,啥其他不干的,可以很灵活的添加新功能。arrnew就是new了,定死了的。
bug嘛。。应该是没啥bug的,以后发现再说