当前位置: 首页 > news >正文

C++——vector容器、动态容器

文章目录

    • vector 定义、输出格式
  • ————vector底层实现————
    • vector迭代器失效问题
  • ————————push_back、pop_back、insert
    • 左值和右值
  • ————————resize底层实现
  • ————————vector()深拷贝、operator =
    • 匿名对象的使用
    • CPP中的两个甜糖——缺省值
    • CPP中的两个甜糖——范围for 所有适用场景
  • ————————swap
  • ————————initializer_list 初始化
    • 多参数的隐式类型转换——插入的知识点

vector 定义、输出格式

vector 可以理解为一个动态的容器,主要目的是为了方便存储。

  • vector 三种种定义格式
第一种 vector<type>x(5)  指定容器内有5个元素,每个元素默认初始化为0vector<int>x(5,1) 指定容器内有5个元素,每个元素初始化为1。
	vector<int>v1(5);vector<int>v2(5, 1);
int arr1[] = {1,2,3,4,5}
vector<int>v3(arr1,arr1+5)
string s1 = "My name is ljy";
vector<int>v4(s1.begin(), s1.end());
输入数组的地址范围(下面是自定义类型,那就需要输入迭代器的范围)
与定义一的意义不同,这里是将输入的地址范围 初始化给v3      直接拿取
	int arr1[] = {1,2,3,4,5};vector<int>v3(arr1, arr1 + 5);string s1 = "My name is ljy";vector<int>v4(s1.begin(), s1.end());
第三种 initializer_list 格式定义
	//vector 一种特殊的定义格式,它会将{} 隐式转化为Initializer_list 类型vector<int>v5 = { 1,2,3,4,5,6,7,8,9,0 };vector<int>v6({ 1,2,3,4,5,6,7,8,9,0 });
  • vector的两种输入格式
第一种角标遍历,vector支持C语言中 采用角标遍历的方法
void printf1(const vector<int>& v)
{for (int i = 0; i < v.size() - 1; i++){cout << v[i] << " ";}cout << endl;
}  //角标遍历
第二种 迭代器遍历
vector最权威的输出格式,采用迭代器可支持 string、vector、list、数组……
void printf3(const vector<int>& v)
{std::vector<int>::const_iterator it = v.begin();while (it != v.end()){cout << *it << " ";it++;}cout << endl;
}
  • 代码运行
#include<iostream>
#include<vector>
#include<string>
using namespace std;void printf1(const vector<int>& v)
{for (int i = 0; i < v.size() - 1; i++){cout << v[i] << " ";}cout << endl;
}  //角标遍历
void printf2(const vector<int>& v)
{for (auto n : v){cout << n << " ";}cout << endl;
}  //auto自动识别
void printf3(const vector<int>& v)
{std::vector<int>::const_iterator it = v.begin();while (it != v.end()){cout << *it << " ";it++;}cout << endl;
}int main()
{vector<int>v1(5);printf1(v1);vector<int>v2(5, 1);printf2(v2);int arr1[] = {1,2,3,4,5};vector<int>v3(arr1, arr1 + 5);printf3(v3);string s1 = "My name is ljy";vector<int>v4(s1.begin(), s1.end());printf3(v4);return 0;
}

————vector底层实现————

  • 默认构造、push_back、capacity、reserve、析构
    vector.h
#pragma once
#include<vector>
#include<iostream>
using std::endl;
using std::cout;
using std::vector;namespace My_vector
{template<class T>class vector{using iterator = T*;public:vector() //如果开始vector一个非空的数组 改怎么办呢?:_start(nullptr),_finish(nullptr),_end_of_strage(nullptr){}size_t capacity() const{return _end_of_strage - _finish;}size_t size() const{return _start - _finish;}void reserve(size_t n){size_t sz = _finish - _start;if (n > capacity()){T* tmp = new T[n];if (_start){memcpy(tmp, _start, sizeof(T) * sz);delete[]_start;}_start = tmp;size = _start + sz;capacity = _start + n;}}void push_back(T& x){if (_finish == _end_of_strage){reserve(capacity() == 0 ? 4 : capacity() * 2);}*_finish = x;  //为什么不是_finish +1呢?_finish += 1;}~vector(){if (_start){delete[] _start;_start = _finish = _end_of_strage = nullptr; // 好习惯,防止野指针}}private:T* _start;T* _finish;T* _end_of_strage;};
}

vector.cpp

#include "vector.h"void printf_v(vector<int>& v)
{for (auto x : v){cout << x << " ";}
}void push_v(vector<int>&v)
{int n = 10;for (int i = 1; i <= n; i++){v.push_back(i);}
}int main()
{vector<int>v = { 0,0,0,0,0 }; // 为为什么vector<int> 这个模板作用域<> 里面的intpush_v(v);printf_v(v);return 0;
}

vector迭代器失效问题

————————push_back、pop_back、insert

insert中pos迭代器失效问题——野指针

		void insert(iterator pos, const T& x) //const——引用的T类型的x对象无法修改{if (_finish == _end_of_strage){int size_t = pos - _start;reserve(capacity() == 0 ? 4 : capacity() * 2);pos = _start + len;}//如果发生扩容会出现迭代器失效的问题assert(pos >= _start);assert(pos <= _finish);//_finish是最后的位置+1iterator end = _finish - 1;while (end>=pos){*(end + 1) = *end;end--;}*pos = x;_finish++;}

在这里插入图片描述

左值和右值

结合string类和对象上const修饰的成员函数

我的疑问是 pos既然经过 reserve之后变为野指针
那为什么 pos = _start + len 这里不会报错呢?
int size_t = pos + _start 这里的pos 是右值,就相当于 我要对pos进行实际化需求的使用 对pos进行解引用
pos = _start + len 而这里的pos 是左值 就相当于我定义了一个pos变量,但是我不使用它,我用来干嘛,用来接受_start+len 的结果值

  • 左值是一个持久化存在的内存地址,int a 创建之后持续化存在
  • 右值是一个临时变量 int a = 5 + 6 5和6在用完之后内存就会被释放

在类和对象形参问题上
T&x 这里得x只能接受左值
const T&x 这里得x既能接受左值也能接受右值

小结:因此 一个常量如:5,6 在赋予T&x上就会出现权限放大得问题
所以许多形参为了避免出现问题 采用const T&x作为形参

————————resize底层实现

		//匿名对象 创建方法 T val = T() 第二个没有函数变量//对于整形如果没有传参,那么为0.//对于内置类型,如果没有传参。那么就传它的默认构造函数void resize(size_t n, T val = T()){if (n < size()){_finish = _start + n;}else{reserve(n);//插入数据for (auto it == _finish; it != _start + n; it++){*it = val;}_finish = _start + n;}}

————————vector()深拷贝、operator =

拷贝构造和赋值运算符重载

		vector(const vector<T>& v)//深拷贝要用引用,值传参会调用拷贝构造,增大代价{reserve(v.capacity());for (const auto &it : v){push_back(it);}}vector<int>& operator=(vector<int>& v){if (this != &v){clear();reserve(v.size());for (auto it = v.begin(); it != v.end(); it++){push_back(*it);}}return *this;}

匿名对象的使用

  • 匿名对象的创建——两种不同的创建形式

T val = T() 自定义模板
int a = int() 默认为0
int a = int(1) 为1
int a ={1}
int a = {}

匿名对象适用场景

void resize(size_t n, T val = T())两个形参,当我们没有传入第二个形参的时候,如果是自定义类型那么就调用它的默认构造,如果是int 那么初始化为0,如果是double那么初始化为0.0

CPP中的两个甜糖——缺省值

  1. 深剖拷贝构造和赋值运算符重载 (深拷贝)

拷贝构造是实现从无到有的构造
而运算符重载是对两个已有的变量/函数/自定义类型进行 替换

  1. 缺省值的适用场景
因此回到代码上来,在调用运算符重载函数的时候,私有成员已经经过初始化,但是拷贝构造私有成员没有初始化。所以在适用拷贝构造的时候必须经过初始化才能适用。

传统写法是在拷贝构造后面加上初始化列表

vector<T>::vector(const vector<T>& v)  // 应该用模板参数T而非固定int: _start(nullptr), _finish(nullptr), _end_of_storage(nullptr)  // 成员初始化
{reserve(v.capacity());for (const auto& it : v)  // 使用const引用提高效率{push_back(it);}
}

但是我们可以利用缺省值来省略这一步,在私有成员命名后面加上缺省值

	private:T* _start = nullptr;T* _finish = nullptr;T* _end_of_storage = nullptr;

CPP中的两个甜糖——范围for 所有适用场景

  1. const auto &it : v ——不拷贝不修改
    容器内的元素只读取不修改
    容器内元素的类型是自定义类型如果是自定义类型步&那么肯定会涉及到拷贝的问题)高效且安全

  2. auto it :v ——修改
    容器内的元素都是可修改的

  3. auto& it:v ——对自身进行修改
    容器对自身需要进行修改的

实例

		vector(const vector<T>& v)//深拷贝要用引用,值传参会调用拷贝构造,增大代价{reserve(v.capacity());for (const auto &it : v){push_back(it);}}

对于深拷贝——且是vector自定义类型,我们不希望修改v容器内的元素,只希望读取然后push_back 到新this指针中那么这是,const auto& it : v 可就太完美了

————————swap

swap——string中swap的使用

		void swap(vector<int>& v){swap(_start, v._start);swap(_finish, v._finish);swap(_end_of_storage, v._storage);}
		vector(const vector<T>& v)//深拷贝要用引用,值传参会调用拷贝构造,增大代价{//swap写法vector <T>& tmp(v.begin(), v.end());swap(tmp;)//传统写法reserve(v.capacity());for (const auto &it : v){push_back(it);}}

————————initializer_list 初始化

		vector(initializer_list<T> il){reserve(il.size());for (const auto& e : il){push_back(e);}}

多参数的隐式类型转换——插入的知识点

class AA {
public:// 单参数构造函数AA(int a) : a_(a) {std::cout << "构造函数被调用" << std::endl;}int a_;
};void printAA(const AA& obj) {std::cout << "AA对象的值是: " << obj.a_ << std::endl;
}int main() {printAA(10); // <--- 注意这里!// 编译器看到你给了一个int,但函数需要AA。// 它发现AA有一个接受int的构造函数,于是就自动做了转换:// 它会临时创建一个AA对象: AA temp(10);// 然后把这个temp传给printAA函数。// 这就是“单参数”的隐式类型转换。
}

现在我们转化为多参数的隐式类型转换

假设我们的 AA 类需要两个参数来构造:
cpp
运行
class AA {
public:// 多参数构造函数AA(int a, int b) : a_(a), b_(b) {}int a_;int b_;
};void printAA(const AA& obj) {std::cout << "AA对象的值是: (" << obj.a_ << ", " << obj.b_ << ")" << std::endl;
}现在问题来了,我们能不能这样调用 printAA 函数?
printAA(1, 2); // 这样行不行?
答案是:不行。为什么不行?因为编译器在进行隐式类型转换时,它只允许一次转换。printAA(1, 2) 这种写法,编译器会认为你想把 1 转换成 AA,然后把 2 也转换成 AA,但函数只需要一个 AA 参数,所以它会直接报错,说参数数量不匹配。如何让 “多参数对象” 也能隐式转换?方法:使用花括号 {} 
printAA({1, 2}); // <--- 这样就可以!
http://www.dtcms.com/a/482942.html

相关文章:

  • C++ 类与对象(下篇)笔记整理
  • 重庆建站服务商漳浦网站开发
  • 深入浅出理解电感:从理论到实践的电路“惯性”元件
  • 分布式事务:基于MQ事务的解决方案详解
  • 无信息先验:贝叶斯分析中的客观基准
  • 公司官网备案流程mysql优化 wordpress
  • 网站建设员课程注册网页版
  • 瑞莎星瑞(Radxa Orion O6) 基于 Android OS 使用 NPU的图片模糊查找APP 开发
  • 户外商品网站制作长沙网站建设的公司
  • 安卓13_ROM修改定制化-----ROM解打包 修改 讲解 导读篇
  • 网站设计亮点望野亭
  • RTC时钟原理
  • STM32运行原理深度解析:从软件到硬件的神奇之旅
  • OpenCV(十一):色彩空间转换
  • 广州安全教育平台网宁波网站seo哪家好
  • 家装网站自己做的平面设计常用网站
  • Three.js轨道控制器完全指南(OrbitControls与TrackballControls)
  • 服务器数据恢复—硬盘黄灯预警,RAID5阵列数据如何恢复?
  • CATIA 转换为 3DXML 全流程:迪威模型网在线转换和本地方转换方法指南
  • 学校门户网站建设的意义做任务分享赚钱的网站
  • 网站个人中心wordpress怎么做手机网站
  • 杂记 15
  • Video Understanding Baseline via papers
  • MySQL架构和存储引擎
  • Zabbix模板,自定义键值监控项,图形
  • 前端js 常见算法面试题目详解
  • 盾思途旅游网站建设免费seo工具
  • 吴江区经济开发区建设工程网站网站对于企业的好处
  • 新的pvc是否可以指定pv, 而这个pv已经被另一个pvc绑定,状态为bound
  • 网站域名在哪里买巩义网站建设案例