• 【STL***vector容器一】


    目录

    基本数据结构 

    构造函数

    析构函数

    属性获取

    总结


    vector容器基本能够支持任何类型的对象,存放在连续空间,同时是一个可以动态增长的数组

    本节中我们主要分析了关于vector满足traits编程规范的定义, 构造函数, 析构函数, 数组的基本信息获取函数

    基本数据结构 

    traits编程规则要求每个使用traits萃取器的都必须自己定义五个嵌套型别

    vector要求连续空间的大小要比用户自己要求的空间大两倍(留着备用,实现动态增长),因此vector也有个弊端就是备用空间也用完后,需要申请更大的空间,然后释放之前的空间。(不过为了动态增长,选择接受..)

    1. template <class T, class Alloc = alloc>
    2. class vector
    3. {
    4. public:
    5. // 定义vector自身的嵌套型别
    6. typedef T value_type;
    7. typedef value_type* pointer;
    8. typedef const value_type* const_pointer;
    9. // 定义迭代器, 这里就只是一个普通的指针
    10. typedef value_type* iterator;
    11. typedef const value_type* const_iterator;
    12. typedef value_type& reference;
    13. typedef const value_type& const_reference;
    14. typedef size_t size_type;
    15. typedef ptrdiff_t difference_type;
    16. ...
    17. protected:
    18. typedef simple_alloc data_allocator; // 设置其空间配置器
    19. iterator start; // 使用空间的头
    20. iterator finish; // 使用空间的尾
    21. iterator end_of_storage; // 可用空间的尾
    22. ...
    23. };

    构造函数

    可以看出,用start来指向一片内存的开头,finish=start+n,一开始e_o_s就是finish

    1. vector() : start(0), finish(0), end_of_storage(0) {} // 默认构造函数
    2. explicit vector(size_type n) { fill_initialize(n, T()); } // 必须显示的调用这个构造函数, 接受一个值
    3. vector(size_type n, const T& value) { fill_initialize(n, value); } // 接受一个大小和初始化值. int和long都执行相同的函数初始化
    4. vector(int n, const T& value) { fill_initialize(n, value); }
    5. vector(long n, const T& value) { fill_initialize(n, value); }
    6. vector(const vector& x); // 接受一个vector参数的构造函数
    1. // 初始化vector的使用空间头和空间的尾
    2. void fill_initialize(size_type n, const T& value)
    3. {
    4. start = allocate_and_fill(n, value); // 初始化并初始化值
    5. finish = start + n;
    6. end_of_storage = finish;
    7. }
    1. // 调用默认的第二配置器分配内存, 分配失败就释放所分配的内存
    2. iterator allocate_and_fill(size_type n, const T& x)
    3. {
    4. iterator result = data_allocator::allocate(n); // 申请n个元素的线性空间.
    5. __STL_TRY // 对整个线性空间进行初始化, 如果有一个失败则删除全部空间并抛出异常.
    6. {
    7. uninitialized_fill_n(result, n, x);
    8. return result;
    9. }
    10. __STL_UNWIND(data_allocator::deallocate(result, n));
    11. }

    vector有一个接受vector参数的构造函数, 调用的是uninitialized_copy执行初始化, 我们在上一篇分析过该函数

    1. vector(const vector& x)
    2. {
    3. start = allocate_and_copy(x.end() - x.begin(), x.begin(), x.end());
    4. finish = start + (x.end() - x.begin()); // 初始化头和尾迭代器位置
    5. end_of_storage = finish;
    6. }
    7. // 同样进行初始化
    8. template <class ForwardIterator>
    9. iterator allocate_and_copy(size_type n, ForwardIterator first, ForwardIterator last)
    10. {
    11. iterator result = data_allocator::allocate(n);
    12. __STL_TRY
    13. {
    14. uninitialized_copy(first, last, result); // 这里采用的是uninitialized_copy, 进行复制.
    15. return result;
    16. }
    17. __STL_UNWIND(data_allocator::deallocate(result, n));
    18. }
    19. #else /* __STL_MEMBER_TEMPLATES */
    20. // 支持两个迭代器表示范围的复制
    21. iterator allocate_and_copy(size_type n,
    22. const_iterator first, const_iterator last)
    23. {
    24. iterator result = data_allocator::allocate(n);
    25. __STL_TRY {
    26. uninitialized_copy(first, last, result);
    27. return result;
    28. }
    29. __STL_UNWIND(data_allocator::deallocate(result, n));
    30. }
    31. #endif /* __STL_MEMBER_TEMPLATES */
    32. };

    接受两个迭代器, 构造一个范围的数据

    1. #ifdef __STL_MEMBER_TEMPLATES
    2. template <class InputIterator>
    3. vector(InputIterator first, InputIterator last) :
    4. start(0), finish(0), end_of_storage(0)
    5. {
    6. range_initialize(first, last, iterator_category(first));
    7. }
    8. #else /* __STL_MEMBER_TEMPLATES */
    9. vector(const_iterator first, const_iterator last) {
    10. size_type n = 0;
    11. distance(first, last, n);
    12. start = allocate_and_copy(n, first, last);
    13. finish = start + n;
    14. end_of_storage = finish;
    15. }
    16. #endif /* __STL_MEMBER_TEMPLATES */

    析构函数

    析构函数就是直接调用deallocate 空间配置器, 从头释放到数据尾部, 最后将内存还给空间配置器.

    vector因为是类, 所以我们并不需要手动的释放内存, 生命周期结束后就自动调用析构从而释放调用空间, 当然我们也可以直接调用析构函数释放内存

    1. void deallocate()
    2. {
    3. if (start)
    4. data_allocator::deallocate(start, end_of_storage - start);
    5. }
    6. // 调用析构函数并释放内存
    7. ~vector()
    8. {
    9. destroy(start, finish);
    10. deallocate();
    11. }

    属性获取

    1. public:
    2. // 获取数据的开始以及结束位置的指针. 记住这里返回的是迭代器, 也就是vector迭代器就是该类型的指针.
    3. iterator begin() { return start; }
    4. iterator end() { return finish; }
    5. // 获取值
    6. reference front() { return *begin(); }
    7. reference back() { return *(end() - 1); }
    8. // 获取右值
    9. const_iterator begin() const { return start; }
    10. const_iterator end() const { return finish; }
    11. const_reference front() const { return *begin(); }
    12. const_reference back() const { return *(end() - 1); }
    13. // 获取基本数组信息
    14. size_type size() const { return size_type(end() - begin()); } // 数组元素的个数
    15. size_type max_size() const { return size_type(-1) / sizeof(T); } // 最大能存储的元素个数
    16. size_type capacity() const { return size_type(end_of_storage - begin()); } // 数组的实际大小

    判断vector是否为空, 并不是比较元素为0, 是直接比较头尾指针

    bool empty() const { return begin() == end(); }	
    

    总结

    vector的迭代器是一个普通的指针
    构造函数的重载满足不同的用户需求
    vector因为是类, 所以在生命周期结束后会自动调用析构函数, 用户不再手动的释放内存, 也不会出现内存泄露的问题, 用户也可以主动调用析构函数释放内存
    finish是指向最后一个元素的后一位地址, 不是直接指向最后一个元素

  • 相关阅读:
    一篇博客带你学会关于Vuex使用
    eMagin:当月产百万片时,4K MicroOLED成本将不是问题
    vue步骤引导插件 vue-intro
    Docker实践笔记04:Tomcat环境DockerFile制作
    Effective C++看书笔记(4):实现
    下班后根本联系不上,这样的员工可以辞退吗
    3.20 关于怎样做好小红书直播的一些技巧【玩赚小红书】
    Java-多线程
    医保结算那些事,医保基金结算的具体违规项目,医保结算审核哪些东西(二)
    C++ 学习(六)函数 及 函数的分文件编写
  • 原文地址:https://blog.csdn.net/weixin_53459056/article/details/126867601