vector的基本使用 + 手搓成员变量 size capacity begin end operator[] reserve扩容 拷贝构造 赋值析构

vector

文章目录

0.是什么

template < class T, class Alloc = allocator<T> > class vector; // generic template

------>是一个类模板




头文件:<vector>






1.构造

cpp 复制代码
	vector<int>v1;//创建一个空vector
	vector<int>v2(10, 1);//创建一个vector,里面有10个1

	vector<int>v3(++v2.begin(), --v2.end());//按迭代器创建一个vector,里面有8个1

    //迭代器换成数组下标
    int arr[] = {1, 2, 3, 4, 5};
    vector<int> v4(arr, arr + 5);


    vector<int> v { 2,3,4,6,1,7 };//后面再说
  • vector<int> 类模板实例化
  • vector<int> v1; 用这个 类 定义了一个对象(变量) v1,并调用默认构造函数。
  • vector<int> v2(10, 1); 用这个 类 定义了一个对象(变量) v1,并调用 构造函数

关于类模板的知识在这里
为什么直接用数组下标也能构造?

  1. 指针满足迭代器的所有要求,指针可以直接用来充当迭代器

  2. vector有(范围)构造函数:

    template <class InputIterator>

    vector(InputIterator first, InputIterator last);

    是一个模板函数,它不关心 传进来的 类型,只要 满足 " 迭代器" 就行。

    ------>通过这个模版生成函数来构造

    模板函数自动类型推导的知识在这里:【纯干货】C++ 模板核心知识点 函数模板 / 类模板语法 + 面试高频坑(类型推)导 / 实例化 / 分离编译)






2.遍历

  1. 下标(实际上是 重载operator
  2. 通过迭代器
  3. 范围for
cpp 复制代码
    vector<int> v { 2,3,4,6,1,7 };
    for (int i = 0; i < v.size(); i++)
        cout << v[i] << ",";

    vector<int>::iterator it = v.begin();
    while (it != v.end())
    {
        cout << *it << endl; 
        ++it;
       }

    for (auto e : v)
        cout << e << " ";

有关类和对象的知识,在这里:

【万字干货】C++类和对象从入门到精通:内存划分/访问限定符/this指针/默认成员函数/const用法/类型转换全拆解,附C++实现顺序表、链表、栈------吃透大厂面试高频考点-CSDN博客

AI问了好久!终于搞懂 C++ 命名空间 / 类 / 对象,90% 初学者都踩过的 getline 天坑全解-CSDN博客

类 = 类型,对象 = 用这个类型创建出来的变量

概念 对应现实事物 说明
头文件(#include <string> 电脑上的文件夹 按功能分类存放代码的容器
命名空间(std 文件夹里的大箱子 防止名字冲突的隔离层
类(std::string 箱子里的小盒子 自定义的类型,描述一类事物的共同属性和行为
对象(std::string s; 用小盒子装的具体物品 用类创建出来的变量,是实际存在的实体
函数(std::getline 箱子里的独立工具 完成特定功能的代码块
全局对象(std::cin 箱子里已经装好的现成工具 标准库提前创建好、可以直接使用的对象
  • C++ 标准库所有的东西都在std这个命名空间里
  • 同名的命名空间会自动合并
  • 不同的标准库头文件,只是把std这个大命名空间分成了不同的小块

3.reserve

文档中说不会缩容

If n is greater than the current vector capacity, the function causes the container to reallocate its storage increasing its capacity to n (or greater).

In all other cases, the function call does not cause a reallocation and the vector capacity is not affected.

cpp 复制代码
    vector<int> v { 2,3,4,6,1,7 };
    cout << "size:" << v.size() << endl << "capacity:" <<  v.capacity() << endl;

    v.reserve(10);
    cout << "size:" << v.size() << endl << "capacity:" << v.capacity() << endl;

    v.reserve(6);
    cout << "size:" << v.size() << endl << "capacity:" << v.capacity() << endl;

size:6
capacity:6
size:6
capacity:10
size:6
capacity:10





4.resize

  • n<size,删除数据
  • n>size,插入数据(插入传的第二个参数,没传就用value_type()),空间(capacity)不够就扩容

value_type是容器的元素类型别名value_type()就是该元素类型的默认构造函数

比如:

  • vector<int>value_type()就是int() → 0
  • vector<string>value_type()就是string() → 空字符串

resize 扩容时,如果你没指定填充值,就用这个默认值填充新元素。

cpp 复制代码
    vector<int> v { 2,3,4,6,1,7 };
    for (auto e : v)
        cout << e << " ";
    cout << endl;
    cout << "size:" << v.size() << endl << "capacity:" <<  v.capacity() << endl;

    v.resize(10); 
    for (auto e : v)
        cout << e << " ";
    cout << endl;
    cout << "size:" << v.size() << endl << "capacity:" << v.capacity() << endl;

    v.resize(2); 
    for (auto e : v)
        cout << e << " ";
    cout << endl;
    cout << "size:" << v.size() << endl << "capacity:" << v.capacity() << endl;







2 3 4 6 1 7
size:6
capacity:6
2 3 4 6 1 7 0 0 0 0
size:10
capacity:10
2 3
size:2
capacity:10





5.插入

5.1push_back

cpp 复制代码
    vector<int> v { 2,3,4,6,1,7 };
    v.push_back(88);

5.2insert

‼️注意点:

insert 后迭代器可能失效insert 可能导致扩容,所有迭代器、引用、指针失效。)

  1. 头插/中间插入(通过迭代器)
  2. 插入多个数据
    1. 通过迭代器
    2. 连续插入相同数据
cpp 复制代码
    vector<int> v1 { 2,3,4,6,1,7 };

    v1.insert(v1.begin() + 3, 5);

//打印

    vector<int> v2 { 1,1,1,1,1,1 };
    v1.insert(v1.begin(), v2.begin(), v2.begin() + 4);

//打印

    v1.insert(v1.begin(), 2, 0);
     
//打印 


2 3 4 5 6 1 7
1 1 1 1 2 3 4 5 6 1 7
0 0 1 1 1 1 2 3 4 5 6 1 7








手搓

private:

cpp 复制代码
// vector.h
namespace lcj
{
    template<class T>
    class vector
    {
    public:
        // ...
    private:
        iterator _start = nullptr;          // 指向数组的起始位置
        iterator _finish = nullptr;         // 指向最后一个有效数据的下一个位置
        iterator _end_of_storage = nullptr; // 指向整个已分配空间的末尾
    };
}





size,capacity,empty

cpp 复制代码
// 返回有效元素个数
size_t size() const { return _finish - _start; }

// 返回总容量
size_t capacity() const { return _end_of_storage - _start; }

// 判断是否为空
bool empty() { return _start == _finish; }





实现迭代器和operator

cpp 复制代码
// vector的迭代器本质就是原生指针!
typedef T* iterator;
typedef const T* const_iterator;

// 普通迭代器(可以修改元素)
iterator begin() { return _start; }
iterator end() { return _finish; }

// const迭代器(只能读不能改)
const_iterator begin() const { return _start; }
const_iterator end() const { return _finish; }


// 普通版本,可以修改元素
T& operator[](size_t i)
{
    assert(i < size()); // 下标越界直接崩溃,比原生数组安全
    return _start[i];
}

// const版本,只能读不能改
const T& operator[](size_t i) const
{
    assert(i < size());
    return _start[i];
}





reserve扩容

此处使用的memcpy 是逐字节拷贝,对于 std::stringvector 等类类型,会导致浅拷贝,后面会细说

❌错误示例:

  • size()是个函数,这时候会计算: _finish - _start;,可是这时候:_start = tmp;start已经不是原来的start了

    ------>计算的size()是错的

cpp 复制代码
		void reserve(size_t n)
		{
			if (n > capacity())
			{
				T* tmp = new T[n];
				memcpy(tmp, _start, sizeof(T) * size());
				delete[] _start;

				_start = tmp;
				_finish = _start + size();
				_end_of_storage = _start + n;
			}
		}



修改方式1:

  • 先改_finish: _finish = tmp + size();,这时候size()还是用的原来的_finish
cpp 复制代码
		void reserve(size_t n)
		{
			if (n > capacity())
			{
				T* tmp = new T[n];
				memcpy(tmp, _start, sizeof(T) * size());
				delete[] _start;

				_finish = tmp + size();

				_start = tmp;
				_end_of_storage = _start + n;
			}
		}



修改方式2:

  • 提前记录下原来的有效元素个数
cpp 复制代码
// vector.h
void reserve(size_t n)
{
    if (n > capacity())
    {
        size_t old_size = size();        // 1. 先记录下原来的有效元素个数
        T* tmp = new T[n];               // 2. 开新空间
        memcpy(tmp, _start, size() * sizeof(T)); // 3. 拷贝数据
        delete[] _start;                 // 4. 释放旧空间

        _start = tmp;                    // 5. 更新三个指针 
        _finish = tmp + old_size;/////////////////////////////////////////////////////////////////
        _end_of_storage = tmp + n;
    }
}





push_back

cpp 复制代码
void push_back(const T& x)
{
    // 检查是否需要扩容
    if (_finish == _end_of_storage)
    {
        reserve(capacity() == 0 ? 4 : capacity() * 2);
    }

    *_finish = x;   // 在当前finish位置存放数据
    ++_finish;      // 移动 finish 指针
}
相关推荐
liulilittle2 小时前
C++ do_div 宏
c++
GHL2842710902 小时前
Qt Creator 19.0.0 (Community)下载
开发语言·qt
之歆2 小时前
Day21_电商详情页核心技术实战:从LESS预处理到复杂交互实现
开发语言·前端·javascript·css·交互·less
Mininglamp_27182 小时前
现在入局Agent开发还来得及吗?
java·开发语言
-To be number.wan2 小时前
算法日记 | STL-MAP
c++·算法
天行健王春城老师2 小时前
制造业生产管控实操指南 PMC体系落地与优化技巧解析
笔记·制造
方也_arkling2 小时前
【Java-Day10】多态
java·开发语言
楼田莉子2 小时前
C++20新特性:Range库
开发语言·c++·后端·学习·c++20