容器与迭代器,C实现《第四篇,vector实现容器》

前言

上一篇介绍了容器的接口。这篇打算一个数组来实现容器的具体接口。

vector

vector动态数组,定义如下:

typedef struct _vector
{
   container_t container;
   type_value_t* _data;
   size_t _size;
   size_t _capacity;
   
} vector_t;
  • container 接口放第一个字段,等于是继承一个父类。
  • type_value_t* 数据节点,由于vector是数组,直接放一个type_value_t 的数组指针好了。
  • size 数组的大小。
  • capacity 数组的容量。
vector接口的实现

根据container_t 的接口定义,需要实现一下接口:

  • container_first
  • container_last
  • container_search
  • container_insert
  • container_remove
  • container_sort
  • container_size

除了container_t的接口外,还需要实现一下迭代器move接口的实现。

  • iterator_move(iter, step)
vector 各种接口实现
  • first
static iterator_t _vector_first (container_t* container) 
{
    vector_t* vec = container;
    return _get_iter(vec->_data, vec);
}

返回一个指向第一节点的迭代器。

  • last
static iterator_t _vector_last (container_t* container) 
{
    vector_t* vec = container;
    return _get_iter((vec->_data + vec->_size -1), vec);
}

返回数组最后一元素的迭代器。

  • search
static iterator_t _vector_search (container_t* container, iterator_t offset, type_value_t find, int (*compare)(type_value_t, type_value_t)) 
{
    
    iterator_t first = offset;
    iterator_t tail = container_tail(container);
    for(; !iterator_equal(first, tail); first=iterator_next(first)) {
        if (compare(iterator_dereference(first), find) == 0) return first;
    }
    // 返回边界的迭代器
    return first;
}

因为type_value_t 是不确定的数据类型,所以需要用户自定义一种比较函数compare(type_value_t, type_value_t),用于查找目标节点。

比较函数返回值如下,两值相等,返回零。前值大于后值,返回一,后值大于前置返回负一。

  • 插入
static int _vector_insert (container_t* container, iterator_t it, type_value_t data)
{
    // head 的位置不能前插
    if (!iterator_is_head(it)){
        
        vector_t* vec = container;
        // 检测一下是否满水?
        if (vec->_size >= vec->_capacity){
            // 注水
            unsigned int require_size = vec->_size + VEC_ALLOC_CHUNK_SIZE;
            type_value_t *new_block = allocate(container_pool(container), require_size * sizeof(type_value_t));

            if (new_block == NULL){
                return -1;
            }

            // 如果整个块要是重新malloc的,那么要重新计算it的位置。
            // 隐藏的bug:地址的差值可能会超过 long 的最大值。
            ptrdiff_t offset = iterator_reference(it) - iterator_reference(container_head(vec));
            // copy 旧数据到新的内存
            memcpy(new_block, vec->_data, vec->_size * sizeof(type_value_t));
            // 释放旧的内存
            deallocate(container_pool(container), vec->_data);
            // 把新内存挂上去
            vec->_data = new_block;
            // 容量值变大。
            vec->_capacity += VEC_ALLOC_CHUNK_SIZE;

            // 更新it的refer。
            void *new_refer = iterator_reference(container_head(vec)) + offset;
            iterator_set_reference(it, new_refer);
        }

        // 继续做插入动作。
        iterator_t last = container_last(container);
        iterator_t it_prev = iterator_prev(it);

        // 挪位
        for (; !iterator_equal(last, it_prev); last = iterator_prev(last)){
            iterator_t last_next = iterator_next(last);
            iterator_assign(last_next, last);
        }
        // 插入
        type_value_t *pt = iterator_reference(it);
        *pt = data;
        vec->_size++;
        return 0;
    }
    return -1;
}

vector 的插入有个需要注意的地方。当向vector插入元素的时候,原来分配的内存可能不够,那么就需要释放原来的内存,申请更大的内存来放入原属,重新申请内存后。原来迭代器find指向的节点地址会变成无效,那么则需要根据旧的内存地址去计算新的内存地址。具体算法参考以上代码。

  • 移除
static int _vector_remove (container_t* container, iterator_t it, type_value_t* rdata) 
{
    if (!iterator_is_boundary(it)){
        
        vector_t *vec = container;

        if (rdata){
            *rdata = iterator_dereference(it);
        }

        // 擦除
        for (;!iterator_equal(it, container_last(vec));it = iterator_next(it)){
            iterator_t it_next = iterator_next(it);
            iterator_assign(it, it_next);
        }
        vec->_size--;
        return 0;
    }
    return -1;
}
  • 排序
static int _vector_sort(container_t* container, int(*compare)(type_value_t, type_value_t)) 
{
    return quick_sort(container_first(container), container_last(container), compare);
}
  • size
static size_t _vector_size (container_t* container) 
{
    return ((vector_t*)container)->_size;
}
  • 迭代器移动接口
static iterator_t _move (iterator_t it, int step) 
{
    type_value_t* pv = iterator_reference(it);

    //return _get_iter((pv + step), iterator_container(it));
    return iterator_set_reference(it, (pv+step));
}
接口绑定

在定义完以上接口后,便可以制作一个vector的容器了。

  • 绑定迭代器的接口的函数:
static iterator_t _get_iter (void* refer, void* vec) {
    return get_iterator(refer, vec, _move);
}

这个refer参数,就是当前节点的指针,参考first的函数的写法:

vector_t* vec = container;
return _get_iter(vec->_data, vec);
  • 绑定容器的接口的函数:
void init_vector(vector_t* vector, pool_t* _pool) {

    // 这个函数调用即是接口函数与container_t 相应的函数字段赋值。
    initialize_container(vector, _vector_first, _vector_last, _vector_search, _vector_insert, _vector_remove,_vector_sort, _vector_size, _pool);
    vector->_size = 0;
    vector->_capacity = VEC_ALLOC_CHUNK_SIZE;
    // 先给水池注点水。
    vector->_data = allocate(container_pool(vector), VEC_ALLOC_CHUNK_SIZE*sizeof(type_value_t));
    return;
}
完了

《第一篇,数据类型》
《第二篇,迭代器》
《第三篇,容器接口》
《第四篇,vector实现的容器》
《第五篇,list实现的容器》
《第六篇,排序》
项目地址

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值