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

C++ STL容器(三) —— 迭代器底层剖析

本篇聚焦于STL中的迭代器,同样基于MSVC源码。


文章目录

  • 迭代器模式
    • 应用场景
    • 实现方式
    • 优缺点
  • UML类图
  • 代码解析
    • list 迭代器
      • const 迭代器
      • 非 const 迭代器
    • vector 迭代器
      • const 迭代器
      • 非const迭代器
    • 反向迭代器
  • 迭代器失效
  • 参考资料


迭代器模式

首先迭代器模式是设计模式中的一种,属于行为型设计模式,简单的迭代器模式结构如下。

应用场景

迭代器模式适合的应用场景有:

  1. 当集合背后为复杂的数据结构,且你希望对客户端隐藏其复杂性时(出于使用便利性或安全性的考虑),可以使用迭代器模式。
    迭代器封装了与复杂数据结构进行交互的细节,为客户端提供多个访问集合元素的简单方法,即具体迭代器内部实现对用户是透明的,用户不用去了解针对每个容器要怎么迭代,只需要用迭代器的简单方法就能通杀。这种方式不仅对客户端来说非常方便,而且能避免客户端在直接与集合交互时执行错误或有害的操作,从而起到保护集合的作用。
  2. 使用该模式可以减少程序中重复的遍历代码。
    重要迭代算法的代码往往体积非常庞大。当这些代码被放置在程序业务逻辑中时,它会让原始代码的职责模糊不清,降低其可维护性。因此,将遍历代码移到迭代器中可使程序代码更加精炼和简洁。
  3. 如果你希望代码能够遍历不同的甚至时无法预知的数据结构,可以使用迭代器模式。
    该模式为集合和迭代器提供了一些通用接口。如果你在代码中使用了这些接口,那么将其他实现了这些接口的集合和迭代器传递给它时,它仍将可以正常运行。

实现方式

  1. 声明迭代器接口。该接口必须提供至少一个方法来获取集合中的下个元素。但为了使用方便,可以添加一些其他方法,例如获取前一个元素、记录当前位置和判断迭代是否结束。(后面也可以看到MSVC中的实现提供了不止一个方法)
  2. 声明集合接口并描述一个获取迭代器的方法,其返回值必须是迭代器接口。如果可能有多组不同的迭代器,可以声明多个类似的方法(比如STL中的容器可以有 begincbegin()rbegin() 等多种获取不同迭代器的方法)。
  3. 为希望使用迭代器进行遍历的集合实现具体迭代器类。迭代器对象必须与单个集合实体链接。链接关系通常通过迭代器的构造函数建立。如STL中的不同容器,有各自不同的迭代器,后面就能看到。
  4. 在你的集合类中实现集合接口。其主要思想是针对特定集合为客户端代码提供创建迭代器的快捷方式。集合对象必须将自身传递给迭代器的构造函数来创建两者之间的链接。这也好理解,迭代器既然想迭代,肯定要持有对应的容器/集合,那么在什么时候给这个迭代器,最好当然是在构造的时候就给迭代器了。
  5. 检查客户端代码,使用迭代器替代所有集合遍历代码。每当客户端需要遍历集合元素时都会获取一个迭代器。

优缺点

优点:

  • 单一职责原则:通过将体积庞大的遍历算法代码抽取为独立的类,可对客户端代码和集合进行整理。
  • 开闭原则:可实现新型的集合和迭代器并将其传给现有代码,无需修改现有代码。
  • 可以并行遍历同一集合,因为每个迭代器对象都包含其自身的遍历状态(读操作并行,写操作还是要同步)。
  • 可以暂停遍历,并在需要时继续。

缺点:

  • 如果你的程序只与简单的集合进行交互,应用该模式可能会事倍功半。
  • 对于某些特殊集合,使用迭代器可能比直接遍历的效率低。

UML类图

下面整理了MSVC中迭代器实现的相关类图,这里暂时先考虑 listvector 两个容器的迭代器。

  1. 可以看到 list 的迭代器和 vector 的迭代器,都有一个公共的基类 _Iterator_base
  2. 这里 list 有 unchecked 和 checked 两种迭代器,checked 迭代器用于 Debug 版本下的运行时错误排查,比如访问越界等。这里包括后面暂时不考虑这块代码,这块具体可以看官方文档的解释。
  3. reverse_iterator 反向迭代器是通过持有正向迭代器,然后重载相关的一些方法来实现逆向迭代的。
  4. const迭代器是非const迭代器的父类,这个也好理解,毕竟只是一个是只读,一个是可读写的,当然在功能上就是包含关系。

代码解析

下面主要分析下 listvector 这两个容器的迭代器以及反向迭代器的实现。

list 迭代器

const 迭代器

  1. 构造函数,第一部分有提到,我们需要把容器和迭代器建立链接,这块就是放在构造函数中做,这里 _Adopt 就是用在 Debug 下的错误检查,在 Release 下是一个空操作。
    _List_unchecked_const_iterator() noexcept : _Ptr() {}_List_unchecked_const_iterator(_Nodeptr _Pnode, const _Mylist* _Plist) noexcept : _Ptr(_Pnode) {this->_Adopt(_Plist);}
  1. 获取指向元素,重载 operator* 因为是链表,返回就是保存在每个节点中的值的引用,这里因为是 const 迭代器,其 reference 等于 const value_type&,如果保存的值是一个结构体/类,也可以用 -> 的方式访问其成员,这里 **this 第一个 * 获取到迭代器本身,第二个 * 调用了 operator* 获取到 _Ptr->_Myval,而 pointer_to 相当于是一个取址,我们就得到一个指向 _Ptr->_Myval 的指针。
    _NODISCARD reference operator*() const noexcept {return _Ptr->_Myval;}_NODISCARD pointer operator->() const noexcept {return pointer_traits<pointer>::pointer_to(**this);}
  1. 向后迭代,这里有前置 ++ 和后置 ++ 前置的话就是返回自己的引用,后置的是返回迭代前的副本。
    _List_unchecked_const_iterator& operator++() noexcept {_Ptr = _Ptr->_Next;return *this;}_List_unchecked_const_iterator operator++(int) noexcept {_List_unchecked_const_iterator _Tmp = *this;_Ptr                                = _Ptr->_Next;return _Tmp;}
  1. 向前迭代list 的迭代器是一个双向的迭代器,所以也支持向前迭代,那么其实就和向后迭代实现差不多了。
    _List_unchecked_const_iterator& operator--() noexcept {_Ptr = _Ptr->_Prev;return *this;}_List_unchecked_const_iterator operator--(int) noexcept {_List_unchecked_const_iterator _Tmp = *this;_Ptr                                = _Ptr->_Prev;return _Tmp;}
  1. 迭代器判等,我们在用迭代器遍历容器的过程经常需要判断当前迭代器是否和 end 迭代器相等,来判断遍历是否结束,因此需要在迭代器中实现判断与另一个迭代器是否相等的方法,对于 list 容器就是简单看迭代器持有的指针是否相等。
    _NODISCARD bool operator==(const _List_unchecked_const_iterator& _Right) const noexcept {return _Ptr == _Right._Ptr;}

非 const 迭代器

非 const 迭代器,其实和 const 迭代器的实现没啥区别,里面也调用了 const 迭代器的方法,除了我们获取内部元素时候,const 迭代器返回的是一个常量引用/常量指针,而非 const 当然应该返回非 const,具体内部也是通过一个 const_cast 来做。

    _NODISCARD reference operator*() const noexcept {return const_cast<reference>(_Mybase::operator*());}_NODISCARD pointer operator->() const noexcept {return pointer_traits<pointer>::pointer_to(**this);}_List_unchecked_iterator& operator++() noexcept {_Mybase::operator++();return *this;}_List_unchecked_iterator operator++(int) noexcept {_List_unchecked_iterator _Tmp = *this;_Mybase::operator++();return _Tmp;}_List_unchecked_iterator& operator--() noexcept {_Mybase::operator--();return *this;}_List_unchecked_iterator operator--(int) noexcept {_List_unchecked_iterator _Tmp = *this;_Mybase::operator--();return _Tmp;}

vector 迭代器

const 迭代器

首先对于构造函数、取元素、前向遍历和后向遍历都和 list 的迭代器差不多(因为随机迭代器的功能肯定包含双向迭代器),除了内部实现有点差别(因为 vector 容器是一块连续的内存,所以用一个指针而不是节点就能找到/遍历这个容器,而指针本身就支持 ++ --)。

    _CONSTEXPR20 _Vector_const_iterator() noexcept : _Ptr() {}_CONSTEXPR20 _Vector_const_iterator(_Tptr _Parg, const _Container_base* _Pvector) noexcept : _Ptr(_Parg) {this->_Adopt(_Pvector);}_NODISCARD _CONSTEXPR20 reference operator*() const noexcept {return *_Ptr;}_NODISCARD _CONSTEXPR20 pointer operator->() const noexcept {return _Ptr;}_CONSTEXPR20 _Vector_const_iterator& operator++() noexcept {++_Ptr;return *this;}_CONSTEXPR20 _Vector_const_iterator operator++(int) noexcept {_Vector_const_iterator _Tmp = *this;++*this;return _Tmp;}_CONSTEXPR20 _Vector_const_iterator& operator--() noexcept {--_Ptr;return *this;}_CONSTEXPR20 _Vector_const_iterator operator--(int) noexcept {_Vector_const_iterator _Tmp = *this;--*this;return _Tmp;}_NODISCARD _CONSTEXPR20 bool operator==(const _Vector_const_iterator& _Right) const noexcept {_Compat(_Right);return _Ptr == _Right._Ptr;}

而随机迭代器,相较于双向迭代器,提供额外的一些功能以支持随机访问。

  1. + += - -= 操作用于一定的随机访问,而对于 vector 容器来说,指针本身就指针这样的操作,所以实现起来也很简单。
    _CONSTEXPR20 _Vector_const_iterator& operator+=(const difference_type _Off) noexcept {_Ptr += _Off;return *this;}_NODISCARD _CONSTEXPR20 _Vector_const_iterator operator+(const difference_type _Off) const noexcept {_Vector_const_iterator _Tmp = *this;_Tmp += _Off;return _Tmp;}_NODISCARD_FRIEND _CONSTEXPR20 _Vector_const_iterator operator+(const difference_type _Off, _Vector_const_iterator _Next) noexcept {_Next += _Off;return _Next;}_CONSTEXPR20 _Vector_const_iterator& operator-=(const difference_type _Off) noexcept {return *this += -_Off;}_NODISCARD _CONSTEXPR20 _Vector_const_iterator operator-(const difference_type _Off) const noexcept {_Vector_const_iterator _Tmp = *this;_Tmp -= _Off;return _Tmp;}_NODISCARD _CONSTEXPR20 difference_type operator-(const _Vector_const_iterator& _Right) const noexcept {return static_cast<difference_type>(_Ptr - _Right._Ptr);}
  1. 迭代器的比较,这里不仅支持了相等的判断,而支持了 < <= > >= 的判断,实现也就是指针的大小判断。
    _NODISCARD bool operator<(const _Vector_const_iterator& _Right) const noexcept {return _Ptr < _Right._Ptr;}_NODISCARD bool operator>(const _Vector_const_iterator& _Right) const noexcept {return _Right < *this;}_NODISCARD bool operator<=(const _Vector_const_iterator& _Right) const noexcept {return !(_Right < *this);}_NODISCARD bool operator>=(const _Vector_const_iterator& _Right) const noexcept {return !(*this < _Right);}
  1. 基于索引的随机访问,作为随机迭代器,当然应该支持基于索引的随机访问
    _NODISCARD _CONSTEXPR20 reference operator[](const difference_type _Off) const noexcept {return *(*this + _Off);}

非const迭代器

和 const 迭代器差不多,也就是返回的是一个非常量引用/非常量指针。

    _NODISCARD _CONSTEXPR20 reference operator*() const noexcept {return const_cast<reference>(_Mybase::operator*());}_NODISCARD _CONSTEXPR20 pointer operator->() const noexcept {return this->_Ptr;}_CONSTEXPR20 _Vector_iterator& operator++() noexcept {_Mybase::operator++();return *this;}_CONSTEXPR20 _Vector_iterator operator++(int) noexcept {_Vector_iterator _Tmp = *this;_Mybase::operator++();return _Tmp;}_CONSTEXPR20 _Vector_iterator& operator--() noexcept {_Mybase::operator--();return *this;}_CONSTEXPR20 _Vector_iterator operator--(int) noexcept {_Vector_iterator _Tmp = *this;_Mybase::operator--();return _Tmp;}_CONSTEXPR20 _Vector_iterator& operator+=(const difference_type _Off) noexcept {_Mybase::operator+=(_Off);return *this;}_NODISCARD _CONSTEXPR20 _Vector_iterator operator+(const difference_type _Off) const noexcept {_Vector_iterator _Tmp = *this;_Tmp += _Off;return _Tmp;}_NODISCARD_FRIEND _CONSTEXPR20 _Vector_iterator operator+(const difference_type _Off, _Vector_iterator _Next) noexcept {_Next += _Off;return _Next;}_CONSTEXPR20 _Vector_iterator& operator-=(const difference_type _Off) noexcept {_Mybase::operator-=(_Off);return *this;}_NODISCARD _CONSTEXPR20 _Vector_iterator operator-(const difference_type _Off) const noexcept {_Vector_iterator _Tmp = *this;_Tmp -= _Off;return _Tmp;}_NODISCARD _CONSTEXPR20 reference operator[](const difference_type _Off) const noexcept {return const_cast<reference>(_Mybase::operator[](_Off));}

反向迭代器

反向迭代器 reverse_iterator 持有正向迭代器,通过实现新的上面的迭代方法,实现方向迭代。

  1. 构造函数,会有一个 current 成员来保存正向迭代器,那么构造函数中就需要传入这个正向迭代器。当然相应的可以通过一个 base 方法获取到其持有的反向迭代器。
    _CONSTEXPR17 reverse_iterator() = default;_CONSTEXPR17 explicit reverse_iterator(_BidIt _Right) noexcept(is_nothrow_move_constructible_v<_BidIt>) // strengthened: current(_STD move(_Right)) {}template <class _Other>_CONSTEXPR17 reverse_iterator(const reverse_iterator<_Other>& _Right) noexcept(is_nothrow_constructible_v<_BidIt, const _Other&>) // strengthened: current(_Right.current) {}template <class _Other>_CONSTEXPR17 reverse_iterator& operator=(const reverse_iterator<_Other>& _Right) noexcept(is_nothrow_assignable_v<_BidIt&, const _Other&>) /* strengthened */ {current = _Right.current;return *this;}_NODISCARD _CONSTEXPR17 _BidIt base() const noexcept(is_nothrow_copy_constructible_v<_BidIt>) /* strengthened */ {return current;}
  1. 获取元素,下面看到 operator* 实现的内容和我们正向迭代器不太一样,首先获取到一个正向迭代器的副本,然后先前置 -- 然后解引用,可以想象对于一个 list 我们传入的一个正向迭代器,然后对其相应的反向迭代器解引用,得到的是前一个节点元素的引用。比如在 list 容器中,提供的 rbegin 得到的是一个 reverse_iterator(end()) 那么解引用后就是链表的最后一个节点。对于 operator-> 就是其实也是一样的,这里用到了条件编译,如果传入的本身就是个指针,那么直接返回指针,如果是个迭代器返回 _Tmp.operator->() 得到的指针。
    _NODISCARD _CONSTEXPR17 reference operator*() const noexcept(is_nothrow_copy_constructible_v<_BidIt> //&& noexcept(*--(_STD declval<_BidIt&>()))) /* strengthened */ {_BidIt _Tmp = current;return *--_Tmp;}_NODISCARD _CONSTEXPR17 pointer operator->() constnoexcept(is_nothrow_copy_constructible_v<_BidIt> //&& noexcept(--(_STD declval<_BidIt&>()))&& _Has_nothrow_operator_arrow<_BidIt&, pointer>) /* strengthened */{_BidIt _Tmp = current;--_Tmp;if constexpr (is_pointer_v<_BidIt>) {return _Tmp;} else {return _Tmp.operator->();}}
  1. 正向/反向迭代,逆向迭代器的正向迭代,其实就相当于对其持有的正向迭代器进行反向迭代,即 --current,而逆向迭代器的反向迭代,当然也就对应着其持有正向迭代器的正向迭代。
    _CONSTEXPR17 reverse_iterator& operator++() noexcept(noexcept(--current)) /* strengthened */ {--current;return *this;}_CONSTEXPR17 reverse_iterator operator++(int) noexcept(is_nothrow_copy_constructible_v<_BidIt> //&& noexcept(--current)) /* strengthened */ {reverse_iterator _Tmp = *this;--current;return _Tmp;}_CONSTEXPR17 reverse_iterator& operator--() noexcept(noexcept(++current)) /* strengthened */ {++current;return *this;}_CONSTEXPR17 reverse_iterator operator--(int) noexcept(is_nothrow_copy_constructible_v<_BidIt> //&& noexcept(++current)) /* strengthened */ {reverse_iterator _Tmp = *this;++current;return _Tmp;}
  1. 随机迭代,也是和正向/反向迭代一样,逆向的迭代器 + 的大小,对应其持有正向迭代器 - 的大小,而对于按索引的随机访问,比如索引是 Off 对应正向迭代器中的索引就是 -_Off-1
    _NODISCARD _CONSTEXPR17 reverse_iterator operator+(const difference_type _Off) constnoexcept(noexcept(reverse_iterator(current - _Off))) /* strengthened */ {return reverse_iterator(current - _Off);}_CONSTEXPR17 reverse_iterator& operator+=(const difference_type _Off) noexcept(noexcept(current -= _Off)) /* strengthened */ {current -= _Off;return *this;}_NODISCARD _CONSTEXPR17 reverse_iterator operator-(const difference_type _Off) constnoexcept(noexcept(reverse_iterator(current + _Off))) /* strengthened */ {return reverse_iterator(current + _Off);}_CONSTEXPR17 reverse_iterator& operator-=(const difference_type _Off) noexcept(noexcept(current += _Off)) /* strengthened */ {current += _Off;return *this;}_NODISCARD _CONSTEXPR17 reference operator[](const difference_type _Off) constnoexcept(noexcept(_STD _Fake_copy_init<reference>(current[_Off]))) /* strengthened */ {return current[static_cast<difference_type>(-_Off - 1)];}

迭代器失效

最后再看下迭代器失效的问题,比如对于一个 vector 容器,我们再用迭代器遍历它的过程中,加入/删除元素就会有迭代器失效的问题,对于加入元素,因为 vector 容器本身是有扩容机制的,那么当 size>capacity 就会触发扩容,那么会申请新的一块内存,然后把元素移动到这个新内存区域。那么会导致我们容器给出的几个指针都发生了改变,但我们的迭代器里还保存着这个旧的指针,但旧的指针指向的已经是一块非法的内存区域了,这就是 vector 插入引发的迭代器失效问题。

vector 如果是删除元素,那么需要考虑到删除元素后,会把后面的元素向前移动,而当前迭代器持有的指针指向的元素可能会发生改变,那么就可能产生出乎你意料的结果。

而对于 list 来说,插入一般不会改变当前迭代器,但删除会导致当前迭代器失效(如果删除了当前迭代器指向的节点,那么当前迭代器持有的指针就是不合法的,迭代器失效)。


参考资料

Checked Iterators
迭代器模式

相关文章:

C++ STL容器(三) —— 迭代器底层剖析

本篇聚焦于STL中的迭代器&#xff0c;同样基于MSVC源码。 文章目录 迭代器模式应用场景实现方式优缺点 UML类图代码解析list 迭代器const 迭代器非 const 迭代器 vector 迭代器const 迭代器非const迭代器 反向迭代器 迭代器失效参考资料 迭代器模式 首先迭代器模式是设计模式中…...

力扣416周赛

举报垃圾信息 题目 3295. 举报垃圾信息 - 力扣&#xff08;LeetCode&#xff09; 思路 直接模拟就好了&#xff0c;这题居然是中等难度 代码 public boolean reportSpam(String[] message, String[] bannedWords) {Map<String,Integer> map new HashMap<>()…...

vue 页面常用图表框架

在 Vue.js 页面中&#xff0c;常见的用于制作图表的框架或库有以下几种&#xff1a; ECharts: 官方网站: EChartsECharts 是一个功能强大、可扩展的图表库&#xff0c;支持多种图表类型&#xff0c;如柱状图、折线图、饼图等。Vue 集成: 可以使用 vue-echarts 插件&#xff0c;…...

spring 注解 - @PostConstruct - 用于初始化工作

PostConstruct 是 Java EE 5 中引入的一个注解&#xff0c;用于标注在方法上&#xff0c;表示该方法应该在依赖注入完成之后执行。这个注解是 javax.annotation 包的一部分&#xff0c;通常用于初始化工作&#xff0c;比如初始化成员变量或者启动一些后台任务。 在 Spring 框架…...

多机器学习模型学习

特征处理 import os import numpy as np import pandas as pd from sklearn.model_selection import train_test_split from sklearn.model_selection import StratifiedShuffleSplit from sklearn.impute import SimpleImputer from sklearn.pipeline import FeatureUnion fr…...

【网页设计】前言

本专栏主要记录 “网页设计” 这一课程的相关笔记。 参考资料&#xff1a; 黑马程序员&#xff1a;黑马程序员pink老师前端入门教程&#xff0c;零基础必看的h5(html5)css3移动端前端视频教程_哔哩哔哩_bilibili 教材&#xff1a;《Adobe创意大学 Dreamweaver CS6标准教材》《…...

STM32巡回研讨会总结(2024)

前言 本次ST公司可以说是推出了7大方面&#xff0c;几乎可以说是覆盖到了目前生活中的方方面面&#xff0c;下面总结下我的感受。无线类 支持多种调制模式&#xff08;LoRa、(G)FSK、(G)MSK 和 BPSK&#xff09;满足工业和消费物联网 (IoT) 中各种低功耗广域网 (LPWAN) 无线应…...

54 螺旋矩阵

解题思路&#xff1a; \qquad 这道题可以直接用模拟解决&#xff0c;顺时针螺旋可以分解为依次沿“右-下-左-上”四个方向的移动&#xff0c;每次碰到“边界”时改变方向&#xff0c;边界是不可到达或已经到达过的地方&#xff0c;会随着指针移动不断收缩。 vector<int>…...

基于STM32与OpenCV的物料搬运机械臂设计流程

一、项目概述 本文提出了一种新型的物流搬运机器人&#xff0c;旨在提高物流行业的物料搬运效率和准确性。该机器人结合了 PID 闭环控制算法与视觉识别技术&#xff0c;能够在复杂的环境中实现自主巡线与物料识别。 项目目标与用途 目标&#xff1a;设计一款能够自动搬运物流…...

[万字长文]stable diffusion代码阅读笔记

stable diffusion代码阅读笔记 获得更好的阅读体验可以转到我的博客y0k1n0的小破站 本文参考的配置文件信息: AutoencoderKL:stable-diffusion\configs\autoencoder\autoencoder_kl_32x32x4.yaml latent-diffusion:stable-diffusion\configs\latent-diffusion\lsun_churches-ld…...

watchEffect工作原理

watchEffect工作原理 自动依赖收集&#xff1a;watchEffect不需要明确指定要观察的响应式数据&#xff0c;它会自动收集回调函数中用到的所有响应式数据作为依赖。即时执行&#xff1a;watchEffect的回调函数会在组件的setup()函数执行时立即执行一次&#xff0c;以便能够立即…...

斐波那契数列

在 Python 3.11 中实现斐波那契数列的常见方式有多种&#xff0c;下面我将展示几种不同的实现方法&#xff0c;包括递归、迭代和使用缓存&#xff08;动态规划&#xff09;来优化递归版本。 1. 递归方式&#xff08;最简单但效率较低&#xff09; def fibonacci_recursive(n)…...

TCP并发服务器的实现

一请求一线程 问题 当客户端数量较多时&#xff0c;使用单独线程为每个客户端处理请求可能导致系统资源的消耗过大和性能瓶颈。 资源消耗&#xff1a; 线程创建和管理开销&#xff1a;每个线程都有其创建和销毁的开销&#xff0c;特别是在高并发环境中&#xff0c;这种开销…...

前端大屏自适应方案

一般后台管理页面&#xff0c;需要自适应的也就是大屏这一个&#xff0c;其他的尺寸我感觉用第三方框架继承好的就挺合适的&#xff0c;当然自适应方案也可以同步到所有页面&#xff0c;但我感觉除了 to c 的项目&#xff0c;不太需要所有页面自适应&#xff0c;毕竟都是查看和…...

16.3 k8s容器cpu内存告警指标与资源request和limit

本节重点介绍 : Guaranteed的pod Qos最高在生产环境中&#xff0c;如何设置 Kubernetes 的 Limit 和 Request 对于优化应用程序和集群性能至关重要。对于 CPU&#xff0c;如果 pod 中服务使用 CPU 超过设置的limits&#xff0c;pod 不会被 kill 掉但会被限制。如果没有设置 li…...

【计算机网络 - 基础问题】每日 3 题(二十)

✍个人博客&#xff1a;Pandaconda-CSDN博客 &#x1f4e3;专栏地址&#xff1a;http://t.csdnimg.cn/fYaBd &#x1f4da;专栏简介&#xff1a;在这个专栏中&#xff0c;我将会分享 C 面试中常见的面试题给大家~ ❤️如果有收获的话&#xff0c;欢迎点赞&#x1f44d;收藏&…...

铰链损失函数

铰链损失函数&#xff08;Hinge Loss&#xff09;主要用于支持向量机&#xff08;SVM&#xff09;中&#xff0c;旨在最大化分类间隔。它的公式为&#xff1a; L ( y , f ( x ) ) max ⁡ ( 0 , 1 − y ⋅ f ( x ) ) L(y, f(x)) \max(0, 1 - y \cdot f(x)) L(y,f(x))max(0,1−…...

项目实战bug修复

实操bug修复记录 左侧侧边栏切换&#xff0c;再次切换侧边栏&#xff0c;右侧未从顶部初始位置展示。地图定位展示&#xff0c;可跳转到设置的对应位置。一个页面多个el-dialog弹出框导致渲染层级出现问题。锚点滚动定位错位问题。动态类名绑定。el-tree树形通过 draggable 属性…...

Git常用指令整理【新手入门级】【by慕羽】

Git 是一个分布式版本控制系统&#xff0c;主要用于跟踪和管理源代码的更改。它允许多名开发者协作&#xff0c;同时提供了强大的功能来管理项目的历史记录和不同版本。本文主要记录和整理&#xff0c;个人理解的Git相关的一些指令和用法 文章目录 一、git安装 & 创建git仓…...

记某学校小程序漏洞挖掘

前言&#xff1a; 遇到一个学校小程序的站点&#xff0c;只在前端登录口做了校验&#xff0c;后端没有任何校验&#xff0c;奇葩弱口令离谱进去&#xff0c;站点里面越权泄露敏感信息&#xff0c;接管账号等漏洞&#xff01;&#xff01;&#xff01; 渗透思路 1.绕过前端 …...

腾讯百度阿里华为常见算法面试题TOP100(3):链表、栈、特殊技巧

之前总结过字节跳动TOP50算法面试题: 字节跳动常见算法面试题top50整理_沉迷单车的追风少年-CSDN博客_字节算法面试题 链表 160.相交链表...

Apache CVE-2021-41773 漏洞复现

1.打开环境 docker pull blueteamsteve/cve-2021-41773:no-cgid docker run -d -p 8080:80 97308de4753d 2.访问靶场 3.使用poc curl http://47.121.191.208:8080/cgi-bin/.%2e/.%2e/.%2e/.%2e/etc/passwd 4.工具验证...

vue-入门速通

setup是最早的生命周期&#xff0c;在vue2里边的data域可以使用this调用setup里面的数据&#xff0c;但是在setup里边不能使用thisvue项目的可执行文件是index&#xff0c;另外运行前端需要npm run vue的三个模块内需要三个不同的结构&#xff0c;里边放置js代码&#xff0c;注…...

【AI大模型】通义大模型API接口实现

目录 一、基础环境安装 &#xff08;一&#xff09;OpenAI Python SDK安装 &#xff08;二&#xff09;DashScope SDK安装 二、OPENAI接口实现 &#xff08;一&#xff09;文本输入 &#xff08;二&#xff09;流式输出 &#xff08;三&#xff09;图像输入 &#xff0…...

CVPR最牛图像评价算法!

本文所涉及所有资源均在 传知代码平台可获取。 目录 概述 一、论文思路 1.多任务学习框架&#xff1a; 2.视觉-语言对应关系&#xff1a; 3.动态损失权重&#xff1a; 4.模型优化和评估&#xff1a; 二、模型介绍 三、详细实现方法 1.图像编码器和语言编码器&#xff08;Image…...

Spring源码-从源码层面讲解传播特性

传播特性:service&#xff1a;REQUIRED&#xff0c;dao:REQUIRED 两个都是required使用的是同一个事务&#xff0c;正常情况&#xff0c;在service提交commit <tx:advice id"myAdvice" transaction-manager"transactionManager"><tx:attributes&…...

Rust调用tree-sitter解析C语言

文章目录 一、Rust 调用 tree-sitter 解析 C 语言代码1. 设置 Rust 项目2. 添加 tree-sitter 依赖3. 编写 Rust 代码4. 运行程序5. 编译出错 二、解决步骤1. 添加 tree-sitter 构建依赖2. 添加 tree-sitter-c 源代码3. 修改 build.rs 以编译 tree-sitter-c 库4. 修改 Cargo.tom…...

奇瑞汽车—经纬恒润 供应链技术共创交流日 成功举办

2024年9月12日&#xff0c;奇瑞汽车—经纬恒润技术交流日在安徽省芜湖市奇瑞总部成功举办。此次盛会标志着经纬恒润与奇瑞汽车再次携手&#xff0c;深入探索汽车智能化新技术的前沿趋势&#xff0c;共同开启面向未来的价值服务与产品新篇章。 面对全球汽车智能化浪潮与产业变革…...

vue3 TagInput 实现

效果 要实现类似于下面这种效果 大致原理 其实是很简单的,我们可以利用 element-plus 组件库里的 el-tag 组件来实现 这里我们可以将其抽离成一个公共的组件,那么现在有一个问题就是通讯问题 这里我们可以利用父子组件之间的通讯,利用 v-model 来实现,父组件传值,子组…...

mysql中的json查询

首先来构造数据 查询department里面name等于研发部的数据 查询语句跟普通的sql语句差不多&#xff0c;也就是字段名要用到path表达式 select * from user u where u.department->$.name 研发部 模糊查询 select * from user u where u.department->$.name like %研发%…...