【C++】STL之list深度剖析及模拟实现
目录
前言
一、list 的使用
1、构造函数
2、迭代器
3、增删查改
4、其他函数使用
二、list 的模拟实现
1、节点的创建
2、push_back 和 push_front
3、普通迭代器
4、const 迭代器
5、增删查改(insert、erase、pop_back、pop_front)
6、构造函数和析构函数
6.1、默认构造
6.2、构造 n 个 val 的对象
6.3、拷贝构造
6.4、迭代器区间构造
6.5、 赋值运算符重载
6.6、析构函数
三、list 模拟实现源代码
四、list 的迭代器失效
五、list 和 vector的对比
前言
- list是可以在常数范围内在任意位置进行插入和删除的序列式容器,并且该容器可以前后双向迭代。
- list的底层是双向链表结构,双向链表中每个元素存储在互不相关的独立节点中,在节点中通过指针指向 其前一个元素和后一个元素。
- list 与 forward_list 非常相似:最主要的不同在于 forward_list 是单链表,只能朝前迭代,已让其更简单高效。
- 与其他的序列式容器相比(array,vector,deque),list 通常在任意位置进行插入、移除元素的执行效率更好。
- 与其他序列式容器相比,list和forward_list最大的缺陷是不支持任意位置的随机访问.
一、list 的使用
1、构造函数
构造函数 | 接口说明 |
list (size_type n, const value_type& val = value_type()) | 构造的list中包含n个值为val的元素 |
list() | 构造空的list |
list (const list& x) | 拷贝构造函数 |
list (InputIterator first, InputIterator last) | 用[first, last)区间中的元素构造list |
int main()
{// 默认构造list<int> lt;lt.push_back(1);lt.push_back(2);lt.push_back(3);lt.push_back(4);// 拷贝构造list<int> lt2(lt);// 构造 n 个节点list<int> lt3(5, 1);// 迭代器区间构造list<int> lt4(lt.begin(), lt.end());return 0;
}
2、迭代器
函数声明 | 接口说明 |
begin + end | 返回第一个元素的迭代器+返回最后一个元素下一个位置的迭代器 |
rbegin + rend | 返回第一个元素的reverse_iterator,即end位置,返回最后一个元素下一个位置的 reverse_iterator,即begin位置 |
int main()
{int a[] = { 1,2,3,4,5,6,7,8,9 };list<int> lt(a, a + 9);auto it = lt.begin();while (it != lt.end()){cout << *it << " ";it++;}cout << endl;return 0;
}
迭代器一般是用来遍历和查找的;
而反向迭代器的使用是类似的,只不过调用的函数换成了 rbegin 和 rend 。
注意:反向迭代器的迭代使用的也是++。但迭代器区间一样是[rbegin, rend);
3、增删查改
函数声明 | 接口说明 |
push_front | 在list首元素前插入值为 val 的元素 |
pop_front | 删除 list 中第一个元素 |
push_back | 在list尾部插入值为 val 的元素 |
pop_back | 删除 list 中最后一个元素 |
insert | 在list position 位置中插入值为 val 的元素 |
erase | 删除list position 位置的元素 |
swap | 交换两个 list 中的元素 |
clear | 清空 list 中的有效元素 |
int main()
{vector<int> v = { 1,2,3,4,5,6,7,8,9 };list<int> lt(v.begin(), v.end());for (auto e : lt) cout << e << " ";cout << endl;lt.push_front(10);lt.push_back(20);for (auto e : lt) cout << e << " ";cout << endl;lt.pop_front();lt.pop_back();for (auto e : lt) cout << e << " ";cout << endl;auto pos = find(lt.begin(), lt.end(), 5);lt.insert(pos, 50);for (auto e : lt) cout << e << " ";cout << endl;pos = find(lt.begin(), lt.end(), 8);lt.erase(pos);for (auto e : lt) cout << e << " ";cout << endl;return 0;
}
4、其他函数使用
函数声明 | 接口说明 |
empty | 检测 list 是否为空,是返回 true ,否则返回 false |
size | 返回 list 中有效节点的个数 |
front | 返回 list 的第一个节点中值的引用 |
back | 返回 list 的最后一个节点中值的引用 |
二、list 的模拟实现
1、节点的创建
template<class T>
struct list_node//节点
{list_node<T>* _next;list_node<T>* _prev;T _data;// 构造函数list_node(const T& x = T()):_next(nullptr), _prev(nullptr), _data(x){}
};
由于节点存储的数据可能是任意类型,所以我们需要将将节点定义为模板类。这里我们需要写一个给缺省值的默认构造函数,便于之后在主类中new一个新节点时直接初始化,同时将两个指针置为空,将数据写入数据域中。
2、push_back 和 push_front
class list
{
public:typedef list_node<T> node;private:node* _head;
}
//尾插
void push_back(const T& x) const
{node* new_node = new node(x);node* tail = _head->_prev;//链接节点之间的关系tail->_next = new_node;new_node->_prev = tail;new_node->_next = _head;_head->_prev = new_node;
}
//头插
void push_front(const T& x)
{node* head = _head->_next;node* new_node = new node(x);_head->_next = new_node;new_node->_prev = _head;new_node->_next = head;head->_prev = new_node;
}
这里模拟的头插和尾插也很简单,因为和我们之前在数据结构时候的双向循环链表是一样的,只需要找到头或者尾,然后链接四个节点间的关系即可。
3、普通迭代器
注意:list 的迭代器是自定义类型,不是原生指针node*。
迭代器为自定义类型,其中*,++等都是通过运算符重载来完成的。
所以我们需要重载的符号:*,->,前置++,后置++,前置--,后置--,!=,==;
template<class T>
struct __list_iterator
{typedef list_node<T> node;typedef __list_iterator<T> self;node* _node;//构造函数__list_iterator(node* n):_node(n){}//重载*运算符T& operator*(){return _node->_val;}T* operator->(){return &_node->_data;}//重载前置++运算符self& operator++(){_node = _node->_next;return *this;}//重载后置++运算符self operator++(int){self tmp(*this);_node = _node->_next;return tmp;}//重载前置--运算符self& operator--(){_node = _node->_prev;return *this;}//重载后置--运算符self operator--(int){self tmp(*this);_node = _node->_prev;return tmp;}//重载!=运算符bool operator!=(const self& s){return _node != s._node;}//重载==运算符bool operator==(const self& s){return _node == s._node;}
};
此处我实现了一个简单的正向迭代器,使用一个模板参数T表示类型。
当普通迭代器封装好了之后,我们需要在list类中来实现它的 begin() 和 end() 方法。由于迭代器的名字一般都是 iterator,而且对于范围for来说,也只能通过 iterator 来转换为迭代器进行遍历。所以这里我们将其typedef为iterator。
template<class T>
class list//链表
{typedef list_node<T> node;
public:typedef __list_iterator<T> iterator;iterator begin(){return iterator(_head->_next);}iterator end(){return iterator(_head);}
private:node* _head;
};
4、const 迭代器
const迭代器与普通迭代器的区别在于const迭代器指向的内容是不能修改的,但是它的指向是可以修改的。
template<class T>
class list//链表
{typedef list_node<T> node;
public:typedef __list_const_iterator<T> const_iterator;const_iterator begin(){return const_iterator(_head->_next);}const_iterator end(){return const_iterator(_head);}
private:node* _head;
};
我们最好的做法就是在__list_iterator 的类模板中的添加两个模板参数,然后再 list 类中 typedef 两份分别将第二个参数分别改成 T& 和 const T& 的类型,本质上就是让编译器根据传入的 Ref 的不同来自动示例化出 const 迭代器类,而我们还需要重载一个->运算符,因为list中可能存储的是自定义类型,这个自定义类型如果要是有多个成员变量的话,我们就需要使用->来解引用访问成员变量,同样还是要区分普通迭代器和const 迭代器,所以就增加了另一个模版参数 Ptr。具体的解决做法如下:
template<class T, class Ref, class Ptr>
struct __list_iterator
{typedef list_node<T> node;typedef __list_iterator<T, Ref, Ptr> self;node* _node;__list_iterator(node* n):_node(n){}Ref operator*()//解引用{return _node->_data;}Ptr operator->(){return &_node->_data;}...
};
然后,最终在链表类中使用如下:
template<class T>
class list//链表
{typedef list_node<T> node;
public:typedef __list_iterator<T, T&, T*> iterator;//普通迭代器typedef __list_iterator<T, const T&, const T*> const_iterator;//const迭代器iterator begin(){return iterator(_head->_next);//匿名对象的返回}const_iterator begin() const{return const_iterator(_head->_next);}iterator end(){return iterator(_head);}const_iterator end() const{return const_iterator(_head);}
private:node* _head;
};
5、增删查改(insert、erase、pop_back、pop_front)
// 指定位置插入
void insert(iterator pos, const T& x)
{node* cur = pos._node;node* prev = cur->_prev;node* new_node = new node(x);prev->_next = new_node;new_node->_prev = prev;new_node->_next = cur;cur->_prev = new_node;
}
// 指定位置删除
iterator erase(iterator pos)
{assert(pos != end());node* prev = pos._node->_prev;node* next = pos._node->_next;prev->_next = next;next->_prev = prev;delete pos._node;return iterator(next);
}
// 尾删
void pop_back()
{erase(--end());
}
// 头删
void pop_front()
{erase(begin());
}
6、构造函数和析构函数
6.1、默认构造
由于后面会频繁对空进行初始化,所以在这里对它进行了封装,方便后面的调用。
void empty_init()//空初始化
{_head = new node;_head->_next = _head;_head->_prev = _head;
}
list()
{empty_init();
}
6.2、构造 n 个 val 的对象
//用n个val构造对象
list(int n, const T& val = T())
{empty_init();for (int i = 0; i < n; i++){push_back(val);}
}
6.3、拷贝构造
//拷贝构造传统写法
list(const list<T>& lt)
{empty_init();for (auto e : lt){push_back(e);}
}
//拷贝构造现代写法
list(const list<T>& lt)
{empty_init();list<T> tmp(lt.begin(), lt.end());swap(tmp);
}
6.4、迭代器区间构造
template <class Iterator>
list(Iterator first, Iterator last)
{empty_init();while (first != last){push_back(*first);++first;}
}
6.5、 赋值运算符重载
//赋值运算符重载
list<T>& operator=(list<T> lt)//注意这里不能用引用
{swap(lt);return *this;
}
6.6、析构函数
//要全部清理掉
~list()
{clear();delete _head;_head = nullptr;
}
//不释放头结点
void clear()
{iterator it = begin();while (it != end()){it = erase(it);//这样也可以//erase(it++);}
}
三、list 模拟实现源代码
template<class T>
struct list_node//节点
{list_node<T>* _next;list_node<T>* _prev;T _data;list_node(const T& x = T()):_next(nullptr), _prev(nullptr), _data(x){}
};
template<class T, class Ref, class Ptr>
struct __list_iterator
{typedef list_node<T> node;typedef __list_iterator<T, Ref, Ptr> self;node* _node;__list_iterator(node* n):_node(n){}Ref operator*()//解引用{return _node->_data;}Ptr operator->(){return &_node->_data;}//前置++self& operator++(){_node = _node->_next;return *this;}//后置++self operator++(int){self tmp(*this);_node = _node->_next;return tmp;}//前置--self& operator--(){_node = _node->_prev;return *this;}//后置--self operator--(int){self tmp(*this);_node = _node->_prev;return tmp;}bool operator!=(const self& s){return _node != s._node;}bool operator==(const self& s){return _node == s._node;}
};
template<class T>
class list//链表
{typedef list_node<T> node;
public:typedef __list_iterator<T, T&, T*> iterator;//普通迭代器typedef __list_iterator<T, const T&, const T*> const_iterator;//const迭代器iterator begin(){return iterator(_head->_next);//匿名对象的返回}const_iterator begin() const{return const_iterator(_head->_next);}iterator end(){return iterator(_head);}const_iterator end() const{return const_iterator(_head);}void empty_init()//空初始化{_head = new node;_head->_next = _head;_head->_prev = _head;}list(){empty_init();}//迭代器区间构造template <class Iterator>list(Iterator first, Iterator last){empty_init();while (first != last){push_back(*first);//push_back使用的前提是要有哨兵位的头结点++first;}}// 交换函数void swap(list<T>& tmp){std::swap(_head, tmp._head);}//现代拷贝构造list(const list<T>& lt){list<T> tmp(lt.begin(), lt.end());swap(tmp);}//现代赋值写法list<T>& operator=(list<T> lt){swap(lt);return *this;}~list()//要全部清理掉{clear();delete _head;_head = nullptr;}void clear()//不释放头结点{iterator it = begin();while (it != end()){it = erase(it);//这样也可以//erase(it++);}}void insert(iterator pos, const T& x){node* cur = pos._node;node* prev = cur->_prev;node* new_node = new node(x);prev->_next = new_node;new_node->_prev = prev;new_node->_next = cur;cur->_prev = new_node;}iterator erase(iterator pos){assert(pos != end());node* prev = pos._node->_prev;node* next = pos._node->_next;prev->_next = next;next->_prev = prev;delete pos._node;return iterator(next);}//尾插void push_back(const T& x) const{//node* new_node = new node(x);//node* tail = _head->_prev;链接节点之间的关系//tail->_next = new_node;//new_node->_prev = tail;//new_node->_next = _head;//_head->_prev = new_node;insert(end(), x);}//头插void push_front(const T& x){//node* head = _head->_next;//node* new_node = new node(x);//_head->_next = new_node;//new_node->_prev = _head;//new_node->_next = head;//head->_prev = new_node;insert(begin(), x);}//尾删void pop_back(){erase(--end());}//头删void pop_front(){erase(begin());}
private:node* _head;
};
四、list 的迭代器失效
当我们使用 erase 进行删除后,此时指向删除位置的迭代器就失效了,再次使用就会令程序崩溃。
因此若要多次删除,则需要在使用后利用 erase 的返回值更新迭代器,这样使用才不会出现错误。
int main()
{vector<int> v = { 1, 2,3,5,4,6 };list<int> lt(v.begin(), v.end());list<int>::iterator pos = find(lt.begin(), lt.end(), 3);for (int i = 0; i < 3; i++){pos = lt.erase(pos); //利用erase的返回值更新迭代器}for (auto e : lt) cout << e << " ";cout << endl;return 0;
}
五、list 和 vector的对比
vector | list | |
底 层 结 构 | 动态顺序表,一段连续空间 | 带头结点的双向循环链表 |
随 机 访 问 | 支持随机访问,访问某个元素效率O(1) | 不支持随机访问,访问某个元素 效率O(N) |
插 入 和 删 除 | 任意位置插入和删除效率低,需要搬移元素,时间复杂 度为O(N),插入时有可能需要增容,增容:开辟新空 间,拷贝元素,释放旧空间,导致效率更低 | 任意位置插入和删除效率高,不 需要搬移元素,时间复杂度为 O(1) |
空 间 利 用 率 | 底层为连续空间,不容易造成内存碎片,空间利用率 高,缓存利用率高 | 底层节点动态开辟,小节点容易 造成内存碎片,空间利用率低, 缓存利用率低 |
迭 代 器 | 原生态指针 | 对原生态指针(节点指针)进行封装 |
迭 代 器 失 效 | 在插入元素时,要给所有的迭代器重新赋值,因为插入 元素有可能会导致重新扩容,致使原来迭代器失效,删 除时,当前迭代器需要重新赋值否则会失效 | 插入元素不会导致迭代器失效, 删除元素时,只会导致当前迭代 器失效,其他迭代器不受影响 |
使 用 场 景 | 需要高效存储,支持随机访问,不关心插入删除效率 | 大量插入和删除操作,不关心随机访问 |
本文要是有不足的地方,欢迎大家在下面评论,我会在第一时间更正。
相关文章:

【C++】STL之list深度剖析及模拟实现
目录 前言 一、list 的使用 1、构造函数 2、迭代器 3、增删查改 4、其他函数使用 二、list 的模拟实现 1、节点的创建 2、push_back 和 push_front 3、普通迭代器 4、const 迭代器 5、增删查改(insert、erase、pop_back、pop_front) 6、构造函数和析构函数 6.1、默认构造…...

解释器风格架构C# 代码
/*解释器风格架构是一种基于组件的设计架构,它将应用程序分解为一系列组件,每个组件负责处理特定的任务。这种架构有助于提高代码的可维护性和可扩展性。以下是如何使用C#实现解释器风格架构的步骤:定义组件:首先,定义…...

第七天:gec6818开发板QT和Ubuntu中QT安装连接sqlite3数据库驱动环境保姆教程
sqlite3数据库简介 帮助文档 SQL Programming 大多数关系型数的操作步骤:1)连接数据库 多数关系型数据库都是C/S模型 (Client/Server)sqlite3是一个本地的单文件关系型数据库,同样也有“连接”的过程 2)操作数据库 作为程序员&am…...

自制网页。
文章目录 注:代码中图片等素材均来自网络,侵删 20230920_213831 index.html <!DOCTYPE html> <html lang="en"> <head><meta charset="UTF-8"><meta name="viewport" content="width=device-width, initial-…...

MySQL单表查询和多表查询
一、单表查询 素材: 表名:worker-- 表中字段均为中文,比如 部门号 工资 职工号 参加工作等 CREATE TABLE worker (部门号 int(11) NOT NULL,职工号 int(11) NOT NULL,工作时间 date NOT NULL,工资 float(8,2) NOT NULL,政治面貌 varchar(10)…...
蓝桥等考Python组别四级006
第一部分:选择题 1、Python L4 (15分) 在Python中,符号“\n”代表( )。 换行空格退格注释正确答案:A 2、Python L4 (15分) 已知大写字母A的ASCII码值为…...
第3章-指标体系与数据可视化-3.2-描述性统计分析与绘图
目录 3.2.1 描述性统计进行数据探索 1. 变量度量类型与分布类型 度量类型 分布类型...

更直观地学习 Git 命令
前言 本文参考于 Learn Git Branching 这个有趣的 Git 学习网站。 在该网站,可以使用 show command 命令展示所有可用命令。 直接访问网站的sandbox。 本地篇 基础篇 git commit git commit将暂存区的修改提交到本地版本库并创建一个新的提交,新提…...
在 Vue 项目中添加字典翻译工具(二)
封装字段翻译组件,可以格式化字典、枚举、字段 优点: 使用简单,一次配置多次使用,缓存降低后端请求次数,扩展性强 没有缓存时造成单页面多次请求解决方法:axios添加缓存请求,防止多次请求&#…...

RDMA Shared Receive Queue(四)
参考知乎文章《RDMA之Shared Receive Queue》:https://zhuanlan.zhihu.com/p/279904125 SRQ SRQ全称为Shared Receive Queue,即共享接受队列。在QP中,SQ用于下发SEND/WRITE/READ等操作,而RQ只用于下发RECV操作,对于本…...

this关键字
作用:出现在成员方法,构造器中代表当前对象的地址,用于访问当前对象的成员变量、成员方法。this出现在 有参数构造器 中的用法 (this.成员变量 局部变量)this出现在 成员方法 中的用法 (this…...

缓存雪崩、缓存击穿、缓存穿透
缓存雪崩 当缓存中大量的键值对同时过期或者Redis宕机了,大量的请求就会直接打到数据库,这种现象就是缓存雪崩 应对策略 有四种,分别是“均匀设置过期时间”、“互斥锁”、“双key策略”、“设置逻辑过期时间,异步更新缓存” …...

Bigemap如何查看历史影像
工具 Bigemap gis office地图软件 BIGEMAP GIS Office-全能版 Bigemap APP_卫星地图APP_高清卫星地图APP 很多人都在寻找历史影像图,这块的需求是非常大,历史影像一般可以用于历史地貌的变迁分析,还原以前的生态场景,对范围面积…...

如何离线安装和使用pymysql操作mysql数据库
一、应用背景 在企业内部网络要使用python操作mysql数据库。然而,python未自带访问MySQL数据库的函数库pymysql,需要另外安装。网上有很多安装pymysql都需要互联网支持。本文主要阐述如何离线安装pymysql,并简要介绍pymysql如何进行mysql操作。 pymysq…...

Prometheus-监控Mysql进阶用法(1)(安装配置)
阿丹: 在开发和生产环境中有可能会出现慢mysql等问题,那么这里就需要我们优秀的程序员来进行监控和解决,那么如何借助云原生的监控系统来完成这个操作呢? 环境描述: 使用一台空白的阿里云服务器2核4G。 服务器基本安装…...

网络安全(黑客技术)自学内容
前言 一、什么是网络安全 网络安全可以基于攻击和防御视角来分类,我们经常听到的 “红队”、“渗透测试” 等就是研究攻击技术,而“蓝队”、“安全运营”、“安全运维”则研究防御技术。 无论网络、Web、移动、桌面、云等哪个领域,都有攻与防…...
linux centos7 安装mongodb7.0.1 及 mongosh2.0.1
下载数据库并解压 wget https://fastdl.mongodb.org/linux/mongodb-linux-x86_64-rhel70-7.0.1.tgz tar -zxf mongodb-linux-x86_64-rhel70-7.0.1.tgz #移动到/usr/local/mongo目录 mv mongodb-linux-x86_64-rhel70-7.0.1 /usr/local/mongodbmongosh 命令行下载 #下载命令行…...

c++ | makefile | 编译 | 链接库
简单记一下 看着人家总结的挺好的 点这...
n个骰子掷出m点的概率,C++实现
一.在骰子游戏中,我们会有猜点数的问题。我们该如何用算法来描述呢? 加入我们当前只有一个骰子,我们该如何算概率呢? 我们现在开始推导一下吧: 首先是一个骰子的情况,我们可以简单的知道n个骰子一共可以掷出…...

【JUC系列-08】深入理解CyclicBarrier底层原理和基本使用
JUC系列整体栏目 内容链接地址【一】深入理解JMM内存模型的底层实现原理https://zhenghuisheng.blog.csdn.net/article/details/132400429【二】深入理解CAS底层原理和基本使用https://blog.csdn.net/zhenghuishengq/article/details/132478786【三】熟练掌握Atomic原子系列基本…...

铭豹扩展坞 USB转网口 突然无法识别解决方法
当 USB 转网口扩展坞在一台笔记本上无法识别,但在其他电脑上正常工作时,问题通常出在笔记本自身或其与扩展坞的兼容性上。以下是系统化的定位思路和排查步骤,帮助你快速找到故障原因: 背景: 一个M-pard(铭豹)扩展坞的网卡突然无法识别了,扩展出来的三个USB接口正常。…...

华为云AI开发平台ModelArts
华为云ModelArts:重塑AI开发流程的“智能引擎”与“创新加速器”! 在人工智能浪潮席卷全球的2025年,企业拥抱AI的意愿空前高涨,但技术门槛高、流程复杂、资源投入巨大的现实,却让许多创新构想止步于实验室。数据科学家…...
【网络】每天掌握一个Linux命令 - iftop
在Linux系统中,iftop是网络管理的得力助手,能实时监控网络流量、连接情况等,帮助排查网络异常。接下来从多方面详细介绍它。 目录 【网络】每天掌握一个Linux命令 - iftop工具概述安装方式核心功能基础用法进阶操作实战案例面试题场景生产场景…...
云计算——弹性云计算器(ECS)
弹性云服务器:ECS 概述 云计算重构了ICT系统,云计算平台厂商推出使得厂家能够主要关注应用管理而非平台管理的云平台,包含如下主要概念。 ECS(Elastic Cloud Server):即弹性云服务器,是云计算…...

阿里云ACP云计算备考笔记 (5)——弹性伸缩
目录 第一章 概述 第二章 弹性伸缩简介 1、弹性伸缩 2、垂直伸缩 3、优势 4、应用场景 ① 无规律的业务量波动 ② 有规律的业务量波动 ③ 无明显业务量波动 ④ 混合型业务 ⑤ 消息通知 ⑥ 生命周期挂钩 ⑦ 自定义方式 ⑧ 滚的升级 5、使用限制 第三章 主要定义 …...

中南大学无人机智能体的全面评估!BEDI:用于评估无人机上具身智能体的综合性基准测试
作者:Mingning Guo, Mengwei Wu, Jiarun He, Shaoxian Li, Haifeng Li, Chao Tao单位:中南大学地球科学与信息物理学院论文标题:BEDI: A Comprehensive Benchmark for Evaluating Embodied Agents on UAVs论文链接:https://arxiv.…...
【Java学习笔记】Arrays类
Arrays 类 1. 导入包:import java.util.Arrays 2. 常用方法一览表 方法描述Arrays.toString()返回数组的字符串形式Arrays.sort()排序(自然排序和定制排序)Arrays.binarySearch()通过二分搜索法进行查找(前提:数组是…...

MFC内存泄露
1、泄露代码示例 void X::SetApplicationBtn() {CMFCRibbonApplicationButton* pBtn GetApplicationButton();// 获取 Ribbon Bar 指针// 创建自定义按钮CCustomRibbonAppButton* pCustomButton new CCustomRibbonAppButton();pCustomButton->SetImage(IDB_BITMAP_Jdp26)…...

iPhone密码忘记了办?iPhoneUnlocker,iPhone解锁工具Aiseesoft iPhone Unlocker 高级注册版分享
平时用 iPhone 的时候,难免会碰到解锁的麻烦事。比如密码忘了、人脸识别 / 指纹识别突然不灵,或者买了二手 iPhone 却被原来的 iCloud 账号锁住,这时候就需要靠谱的解锁工具来帮忙了。Aiseesoft iPhone Unlocker 就是专门解决这些问题的软件&…...

Vue2 第一节_Vue2上手_插值表达式{{}}_访问数据和修改数据_Vue开发者工具
文章目录 1.Vue2上手-如何创建一个Vue实例,进行初始化渲染2. 插值表达式{{}}3. 访问数据和修改数据4. vue响应式5. Vue开发者工具--方便调试 1.Vue2上手-如何创建一个Vue实例,进行初始化渲染 准备容器引包创建Vue实例 new Vue()指定配置项 ->渲染数据 准备一个容器,例如: …...