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

【C++】list的模拟实现

文章目录

    • 1.list 底层
    • 2. list的模拟实现
      • 1. list_node 类设计
      • 2. list类如何调用类型
      • 3 .push_back(正常实现)
      • 4. 迭代器的实现
        • 第一个模板参数T
        • const迭代器
        • 第二个模板参数Ref
        • 第三个模板参数Ptr
        • 对list封装的理解
      • 5. insert
      • 6.push_back与 push_front(复用)
      • 7. erase
      • 8. pop_back与pop_front (复用)
      • 9. clear 清空数据
      • 10. 迭代器区间构造
      • 12. 拷贝构造
        • 传统写法
        • 现代写法
      • 13. 赋值
    • 3.完整代码

1.list 底层

list为任意位置插入删除的容器,底层为带头双向循环链表

begin() 代表第一个结点,end()代表最后一个结点的下一个

2. list的模拟实现

1. list_node 类设计

template<class T>struct list_node{list_node<T>* _next;list_node<T>* _prev;T _data;};

C++中,Listnode作为类名,而next和prev都是类指针,指针引用成员时使用->,而对象引用成员时使用 .
通过显示实例化,将两个类指针指定类型为T


2. list类如何调用类型

Listnode 代表类型
Listnode 代表类名 + 模板参数 才是类型
而_head 以及创建新节点前都需加上类型

3 .push_back(正常实现)

void push_back(const T&x)//尾插{node* newnode = new node(x);node* tail = _head->_prev;tail->_next = newnode;newnode->_prev = tail;newnode->_next = _head;_head->_prev = newnode;}

当我们想要创建一个节点时,需要调用node的构造函数
typedef list_node node ,node是由 list_node 类提供的


list_node(const T& x=T())//list类的构造函数:_next(nullptr), _prev(nullptr), _data(x){}

最好在构造函数处提供全缺省,对于内置类型int可以使用0,但对于自定义类型来说就不可以,所以为了满足泛型的要求,使用匿名对象调用默认构造函数

4. 迭代器的实现

若在数组上有一个int类型的指针,解引用是int类型的数据,再++可以加载下一个位置,因为物理空间是连续的


同样若在链表上,解引用类型为 node,再++不能到下一个节点,因为物理空间不连续
所以构造迭代器通过封装节点的指针来进行构造 (后面会讲)

第一个模板参数T

创建一个_list_iterator的类,来实现迭代器功能


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->_data;}_list_iterator<T>& operator++()//返回迭代器{_node = _node->_next;//指向下一个节点return *this;}bool operator!=(const self&s){return _node != s._node;}};

在list类中,调用迭代器实现begin()和end()功能
typedef _list_iterator<T,T&,T*> iterator,
通过typedef 将_list_node类模板定义为iterator


iterator begin(){return iterator(_head->_next);//通过匿名对象访问第一个数据}iterator end(){return iterator(_head);//通过匿名对象访问最后一个数据的下一个}

在list类中实现begin()和end(),内部调用_list_node类的构造函数


在这里插入图片描述

const迭代器

在这里插入图片描述

假设第一个代表的是T * ,而第二个代表的 T * const,保护迭代器本身不可以被修改,而我们想要保护迭代器指向的内容不可被修改即 const T*


复制_list_iterator类中的内容,并将名字修改为const_list_iterator
只需修改*operator类型为cosnt T& ,说明解引用后的数据返回不能被修改


template<class T>struct _list_const_iterator{typedef list_node<T> node;typedef _list_const_iterator<T> self;node* _node;_list_const_iterator(node* n):_node(n){}const T& operator*()//解引用 {return _node->_data;}self& operator++()//前置++{_node = _node->_next;//指向下一个节点return *this;}self& operator++(int)//后置++{self ret = *this;_node = _node->_next;return ret;}self& operator--()//前置--{_node = _node->_prev;return *this;}self& operator--(int)//后置--{self ret = *this;_node = _node->_prev;return ret;}bool operator!=(const self& s)//!={return _node != s._node;}bool operator==(const self& s)//=={return _node == s._node;}};

在这里插入图片描述

第二个模板参数Ref

迭代器和const迭代器只有 *operator 的返回值不同,
只需在模板中添加一个参数即可使用一个迭代器实现迭代器和const 迭代器的功能

在这里插入图片描述


在这里插入图片描述

第三个模板参数Ptr

对于内置类型int使用解引用找到对应数据,而自定义类型需使用->寻找下一个节点


AA作为自定义类型,想要找到下一个节点需要使用->,在迭代器中 重载 - >

it->_a1,实际上是 it->->_a1,
it->返回值为AA* ,再通过这个指针使用->指向_a1
但是为了增强可读性,省略了一个->
it->_a1 实际上为 it.operator->()->._a1


在这里插入图片描述


在这里插入图片描述

对list封装的理解

在不考虑封装的情况下,两者等价


从物理空间上来看,it与pnode都是指向1的地址



pnode作为一个原生指针,解引用指针就会拿到这个地址,找到这个地址指向空间的内容
++pnode,找到下一个节点的地址,但是下一个节点不一定是要的节点
*it 识别成为自定义类型就会调用函数

5. insert

void insert(iterator pos,const T&x)//在pos位置前插入x{node* cur = pos._node;node* prev = cur->_prev;node* newnode = new node(x);prev->_next = newnode;newnode->_prev = prev;newnode->_next = cur;cur->_prev = newnode;}

6.push_back与 push_front(复用)

两者都可以通过复用 insert的方式实现

void push_back(const T&x)//尾插{/*node* tail = _head->_prev;node* newnode = new node(x);tail->_next = newnode;newnode->_prev = tail;newnode->_next = _head;_head->_prev = newnode;*/insert(end(), x);}void push_front(const T&x)//头插{insert(begin(), x);}

7. erase

void erase(iterator pos)//删除pos位置{//头节点不可以删除assert(pos != end());node* cur = pos._node;node* prev = cur->_prev;node* next = cur->_next;prev->_next = next;next->_prev = prev;delete cur;}

由于头节点不可以删除,所以使用assert断言的方式

8. pop_back与pop_front (复用)

                void pop_back()//尾删{erase(--end());}void pop_front()//头删{erase(begin());}

9. clear 清空数据

void clear()//清空数据//但是要注意不要把head清掉{iterator it = begin();while (it != end()){it=erase(it);//为了防止迭代器失效设置返回值//返回值为当前节点的下一个}}

迭代器失效是指迭代器所指向的节点失效 即节点被删除了
erase函数执行后,it所指向的节点被删除,因此it无效,在下一次使用it时,必须先给it赋值



为了防止迭代器失效所以使erase设置返回值,返回值为当前节点的下一个

10. 迭代器区间构造

void empty_init(){_head = new node;_head->_next = _head;_head->_prev = _head;}
template <class Iterator>list(Iterator first, Iterator last){empty_init();while (first != last){push_back(*first);++first;}}

想要尾插的前提时,需要有头节点的存在,所以设置一个函数对头节点初始化


12. 拷贝构造

传统写法

void empty_init(){_head = new node;_head->_next = _head;_head->_prev = _head;}
list(const list<T>& It)//拷贝构造  传统写法{empty_init();//初始化头节点for (auto e : It){push_back(e);}}

现代写法

void swap(list<T>& tmp){std::swap(_head, tmp._head);}list(const list<T>& It)//拷贝构造现代写法{empty_init();//将头节点初始化list<T> tmp(It.begin(), It.end());swap(tmp);}

通过调用std中的swap来达到交换的目的

13. 赋值

void swap(list<T>& tmp){std::swap(_head, tmp._head);}
list<T>& operator=(list<T> It){swap(It);return *this;}

参数不可以使用 list &,虽然可以达到赋值的目的,但是It的值也会发生改变


在这里插入图片描述


在这里插入图片描述

3.完整代码

#include<iostream>
#include<list>
#include<assert.h>
using namespace std;
namespace yzq
{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 ret = *this;_node = _node->_next;return ret;}self& operator--()//前置--{_node = _node->_prev;return *this;}self& operator--(int)//后置--{self ret = *this;_node = _node->_prev;return ret;}bool operator!=(const self&s)//!={return _node != s._node;}bool operator==(const self& s)//=={return _node == s._node;}};//const迭代器//template<class T>//struct _list_const_iterator//{//	typedef list_node<T> node;//	typedef _list_const_iterator<T> self;//	node* _node;//	_list_const_iterator(node* n)//		:_node(n)//	{//	}//	const T& operator*()//解引用 //	{//		return _node->_data;//	}//	self& operator++()//前置++//	{//		_node = _node->_next;//指向下一个节点//		return *this;//	}//	self& operator++(int)//后置++//	{//		self ret = *this;//		_node = _node->_next;//		return ret;//	}//	self& operator--()//前置--//	{//		_node = _node->_prev;//		return *this;//	}//	self& operator--(int)//后置--//	{//		self ret = *this;//		_node = _node->_prev;//		return ret;//	}//	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迭代器//typedef _list_const_iterator<T> const_iterator;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);++first;}}//list(const list<T>& It)//拷贝构造//{//	empty_init();//初始化头节点//	for (auto e : It)//	{//		push_back(e);//	}//}void swap(list<T>& tmp){std::swap(_head, tmp._head);}list(const list<T>& It)//拷贝构造现代写法{empty_init();//将头节点初始化list<T> tmp(It.begin(), It.end());swap(tmp);}list<T>& operator=(list<T> It)//赋值{swap(It);return *this;}~list()//析构函数{//将头节点也要释放掉clear();delete _head;_head = nullptr;}void clear()//清空数据//但是要注意不要把head清掉{iterator it = begin();while (it != end()){it=erase(it);//为了防止迭代器失效设置返回值//返回值为当前节点的下一个}}void push_back(const T&x)//尾插{/*node* tail = _head->_prev;node* newnode = new node(x);tail->_next = newnode;newnode->_prev = tail;newnode->_next = _head;_head->_prev = newnode;*/insert(end(), x);}void push_front(const T&x)//头插{insert(begin(), x);}void insert(iterator pos,const T&x)//在pos位置前插入x{node* cur = pos._node;node* prev = cur->_prev;node* newnode = new node(x);prev->_next = newnode;newnode->_prev = prev;newnode->_next = cur;cur->_prev = newnode;}iterator erase(iterator pos)//删除pos位置{//头节点不可以删除assert(pos != end());node* cur = pos._node;node* prev = cur->_prev;node* next = cur->_next;prev->_next = next;next->_prev = prev;delete cur;return iterator(next);}void pop_back()//尾删{erase(--end());}void pop_front()//头删{erase(begin());}iterator begin(){return iterator(_head->_next);//通过匿名对象访问第一个数据}iterator end(){return iterator(_head);//通过匿名对象访问最后一个数据的下一个}const_iterator begin()const{return const_iterator(_head->_next);}const_iterator end()const{return const_iterator(_head);}private:node* _head;};/*void test(const list<int>&e){list<int>::const_iterator it = e.begin();while (it != e.end()){cout << *it << " ";++it;}cout << endl;}void test2(){const list<int> v;test(v);}*///void test1()//{//	list<int> v;//	v.push_back(1);//	v.push_back(2);//	v.push_back(3);//	v.push_back(4);//	list<int>::iterator it= v.begin();//	while (it != v.end())//	{//		cout << *it << " ";//		++it;//	}//	cout << endl;//}/*struct AA{int	_a1;int _a2;AA(int a1=0,int a2=0):_a1(a1),_a2(a2){}};*//*void test3(){list<AA>v;v.push_back(AA(1, 1));v.push_back(AA(2, 2));v.push_back(AA(3, 3));list<AA>::iterator it = v.begin();while (it != v.end()){cout << it->_a1 << " :"<<it->_a2<<" ";cout << endl;it++;}}*///void test4()//{//	list<int> v;//	v.push_back(1);//	v.push_back(2);//	v.push_back(3);//	v.push_back(4);// 1 2 3 4//	for (auto e : v)//	{//		cout << e << " ";//	}//	cout << endl;//	v.clear();//	v.push_back(4);//  4//	for (auto e : v)//	{//		cout << e << " ";//	}//}//void test4()//{//	list<int> v;//	v.push_back(1);//	v.push_back(2);//	v.push_back(3);//	v.push_back(4);// 1 2 3 4//	for (auto e : v)//	{//		cout << e << " ";//	}//	cout << endl;//	list<int> v2(v);//	for (auto e : v2)// 1 2 3 4//	{//		cout << e << " ";//	}//}void test4(){list<int> v;v.push_back(1);v.push_back(2);//v1 = 1 2list<int> v2;v2.push_back(5);v2.push_back(6);//v2=5 6v2 = v;for (auto e : v2)// v2=1 2 {cout << e << " ";}cout << endl;for (auto e : v)// v1=1 2{cout << e << " ";}}
}	int main()
{yzq::test4();return 0;
}

相关文章:

【C++】list的模拟实现

文章目录1.list 底层2. list的模拟实现1. list_node 类设计2. list类如何调用类型3 .push_back(正常实现)4. 迭代器的实现第一个模板参数Tconst迭代器第二个模板参数Ref第三个模板参数Ptr对list封装的理解5. insert6.push_back与 push_front(复用)7. erase8. pop_back与pop_fro…...

Python连接es笔记三之es更新操作

这一篇笔记介绍如何使用 Python 对数据进行更新操作。 对于 es 的更新的操作&#xff0c;不用到 Search() 方法&#xff0c;而是直接使用 es 的连接加上相应的函数来操作&#xff0c;本篇笔记目录如下&#xff1a; 获取连接update()update_by_query()批量更新UpdateByQuery()…...

哪个牌子的蓝牙耳机音质好?音质比较好的蓝牙耳机排名

蓝牙耳机经过多年发展&#xff0c;无论是在外观设计还是性能配置上都有很大的进步&#xff0c;越来越多的蓝牙耳机开始注重音质表现&#xff0c;逐渐有HIFI音质、无损音质出现在大众视野。那么哪个牌子的蓝牙耳机音质好&#xff1f;接下来&#xff0c;我来给大家分享几款音质比…...

Qt实用技巧:Qt中浮点数的相等比较方式(包括单精度和双精度)

若该文为原创文章&#xff0c;转载请注明原文出处 本文章博客地址&#xff1a;https://hpzwl.blog.csdn.net/article/details/129464152 红胖子(红模仿)的博文大全&#xff1a;开发技术集合&#xff08;包含Qt实用技术、树莓派、三维、OpenCV、OpenGL、ffmpeg、OSG、单片机、软…...

【数据结构初阶】双向循环链表

目录一.链表的分类二.与单链表相比三.实现增删查改1.双向循环链表结构的创建2.创建新节点3.初始化链表4.头插和尾插5.判断链表是否为空6.头删和尾删7.打印函数8.查找函数9.删除pos位置节点10.在pos前位置插入数据11.优化升级一.链表的分类 链表可有根据单向双向、有无哨兵位、…...

0104BeanDefinition合并和BeanClass加载-Bean生命周期详解-spring

文章目录1 前言2 BeanDefinition合并2.1 BeanDefinition合并在做什么&#xff1f;2.2 BeanDefinition怎么合并2.3 示例演示3 Bean Class 加载后记1 前言 下面要介绍的阶段&#xff0c;都是在调用getBean()从容器中获取bean对象的过程中发生的操作&#xff0c;我们需要更多的去…...

Java集合进阶(三)

文章目录一、Map1. 概述2. 基本功能3. 遍历4. 遍历学生对象5. 集合嵌套6. 统计字符出现次数二、Collections1. 常用方法2. 学生对象排序三、模拟斗地主一、Map 1. 概述 Interface Map<K, V>&#xff1a;K 是键的类型&#xff0c;V 是值的类型。 将键映射到值的对象&…...

【网络】什么是RPC?RPC与HTTP有什么关系?

文章目录RPC是什么RPC和HTTP的关系和区别[附]关于REST论文中提到的"HTTP不是RPC"重点参考 凤凰架构-远程过程调用 既然有HTTP为什么还要有RPC&#xff1f; RPC是什么 RPC(Remote Procedure Call)&#xff1a;即远程过程调用&#xff0c;目的是为了让计算机能够跟调用…...

[手撕数据结构]栈的深入学习-java实现

CSDN的各位uu们你们好,今天千泽带来了栈的深入学习,我们会简单的用代码实现一下栈, 接下来让我们一起进入栈的神奇小世界吧!0.速览文章一、栈的定义1. 栈的概念2. 栈的图解二、栈的模拟实现三.栈的经典使用场景-逆波兰表达式总结一、栈的定义 1. 栈的概念 栈&#xff1a;一种…...

2.线性表的顺序表示

数据结构很重要&#xff01; 数据结构很重要&#xff01;&#xff01;&#xff01; 数据结构很重要&#xff01;&#xff01;&#xff01;&#xff01; 思考 1.线性表的顺序表示内容有哪些&#xff1f;&#xff08;What&#xff09; 2.为什么要学线性表的顺序表示? ? (Why)…...

eps文件删除了能恢复吗?恢复误删eps文件的三种方法

eps文件格式专为矢量图像和图形而设计。虽然没有被广泛使用&#xff0c;但它仍然受到各种插画家和平面设计师的钟爱。eps文件十分适合创建徽标和商标设计&#xff0c;主要应用见于广告牌、海报和横幅。可是在使用设备过程中&#xff0c;难免会遇到数据丢失问题&#xff0c;如果…...

【C++】运算符重载练习——Date 类

文章目录&#x1f449;日期类介绍&#x1f448;&#x1f449;日期类实现&#x1f448;&#x1f4d5; 成员变量&#x1f4d5; 构造函数&#x1f4d5; 对应月份天数&#x1f4d5; 赋值重载&#x1f4d5; 比较运算符重载&#x1f4d5; 计算 运算符重载&#x1f449;源代码&#x1…...

Redis学习(13)之Lua脚本【环境准备】

文章目录一 Lua入门环境准备1.1 Lua简介1.2 Linux 系统安装Lua1.2.1 Lua 下载1.2.2 Lua 安装1.3 Hello World1.3.1 命令行模式1.3.2 脚本文件模式1.3.3 两种脚本运行方式1.4 Win安装Lua1.4.1 LuaForWindows的安装1.4.2 SciTE修改字体大小1.4.3 SciTE中文乱码1.4.4 SciTE快捷键工…...

关于BLE的一些知识总结

数据包长度对于BLE4.0/4.1来说&#xff0c;一个数据包的有效载荷最大为20字节对于BLE4.2以上&#xff0c;数据包的有效载荷扩大为251字节传输速率在不考虑跳频间隔的情况下&#xff0c;最大传输速率为&#xff1a;1&#xff09;BLE4.0/4.1的理论吞吐率为39kb/s&#xff1b;2&am…...

Spring框架源码分析一

如何看源码&#xff08;方法论&#xff09;不要忽略源码中的注释使用翻译工具先梳理脉络&#xff0c;然后梳理细节即总分总&#xff0c;先总体过一遍&#xff0c;再看细节&#xff0c;再做一个总结大胆猜测&#xff08;8分靠猜&#xff09;&#xff0c;小心验证&#xff0c;再调…...

CSS常用内容总结(扫盲)

文章目录前言相关概念【了解】脚本语言什么是脚本语言脚本语言有什么特点常见的脚本语言什么是动态语言&#xff0c;什么是静态语言动态语言和静态语言两者之间有何区别CSSCSS是什么CSS的特点一、CSS代码怎么写基本语法规则引入方式内部样式内联样式表外部样式代码风格二、CSS的…...

Java启蒙之语言基础

目录 一.Java标识符和关键字 1.1Java标识符 1.2Java关键字 二.数据类型和变量的概述和关系 2.1Java变量 2.2Java的数据类型 2.2.1数据类型的分类的概述 2.2.2数据类型的转换 3.Java运算符 总结 &#x1f63d;个人主页&#xff1a;tq02的博客_CSDN博客-领域博主 &#…...

数据库系统--T-SQL数据查询功能-多表查询(超详细/设计/实验/作业/练习)

目录课程名&#xff1a;数据库系统内容/作用&#xff1a;设计/实验/作业/练习学习&#xff1a;T-SQL数据查询功能-多表查询一、前言二、环境与设备三、内容四、内容练习题目&#xff1a;对应题目答案&#xff1a;五、总结课程名&#xff1a;数据库系统 内容/作用&#xff1a;设…...

Spring Boot 3.0系列【14】核心特性篇之Configuration相关注解汇总介绍

有道无术,术尚可求,有术无道,止于术。 本系列Spring Boot版本3.0.3 源码地址:https://gitee.com/pearl-organization/study-spring-boot3 文章目录 前言@Configuration@ConfigurationProperties@EnableConfigurationProperties@ConfigurationPropertiesScan@Configuratio…...

[ubuntu][jetson]给jetson增加swap空间类似于给windows加虚拟内存

具体操作如下&#xff1a; #打开性能模式 sudo nvpmodel -m 0 && sudo jetson_clocks #增加swap空间&#xff0c;防止爆内存 swapoff -a sudo fallocate -l 15G /swapfile sudo chmod 600 /var/swapfile sudo mkswap /swapfile sudo swapon /swapfile…...

挑战杯推荐项目

“人工智能”创意赛 - 智能艺术创作助手&#xff1a;借助大模型技术&#xff0c;开发能根据用户输入的主题、风格等要求&#xff0c;生成绘画、音乐、文学作品等多种形式艺术创作灵感或初稿的应用&#xff0c;帮助艺术家和创意爱好者激发创意、提高创作效率。 ​ - 个性化梦境…...

Leetcode 3576. Transform Array to All Equal Elements

Leetcode 3576. Transform Array to All Equal Elements 1. 解题思路2. 代码实现 题目链接&#xff1a;3576. Transform Array to All Equal Elements 1. 解题思路 这一题思路上就是分别考察一下是否能将其转化为全1或者全-1数组即可。 至于每一种情况是否可以达到&#xf…...

质量体系的重要

质量体系是为确保产品、服务或过程质量满足规定要求&#xff0c;由相互关联的要素构成的有机整体。其核心内容可归纳为以下五个方面&#xff1a; &#x1f3db;️ 一、组织架构与职责 质量体系明确组织内各部门、岗位的职责与权限&#xff0c;形成层级清晰的管理网络&#xf…...

【SQL学习笔记1】增删改查+多表连接全解析(内附SQL免费在线练习工具)

可以使用Sqliteviz这个网站免费编写sql语句&#xff0c;它能够让用户直接在浏览器内练习SQL的语法&#xff0c;不需要安装任何软件。 链接如下&#xff1a; sqliteviz 注意&#xff1a; 在转写SQL语法时&#xff0c;关键字之间有一个特定的顺序&#xff0c;这个顺序会影响到…...

AI编程--插件对比分析:CodeRider、GitHub Copilot及其他

AI编程插件对比分析&#xff1a;CodeRider、GitHub Copilot及其他 随着人工智能技术的快速发展&#xff0c;AI编程插件已成为提升开发者生产力的重要工具。CodeRider和GitHub Copilot作为市场上的领先者&#xff0c;分别以其独特的特性和生态系统吸引了大量开发者。本文将从功…...

云原生玩法三问:构建自定义开发环境

云原生玩法三问&#xff1a;构建自定义开发环境 引言 临时运维一个古董项目&#xff0c;无文档&#xff0c;无环境&#xff0c;无交接人&#xff0c;俗称三无。 运行设备的环境老&#xff0c;本地环境版本高&#xff0c;ssh不过去。正好最近对 腾讯出品的云原生 cnb 感兴趣&…...

html css js网页制作成品——HTML+CSS榴莲商城网页设计(4页)附源码

目录 一、&#x1f468;‍&#x1f393;网站题目 二、✍️网站描述 三、&#x1f4da;网站介绍 四、&#x1f310;网站效果 五、&#x1fa93; 代码实现 &#x1f9f1;HTML 六、&#x1f947; 如何让学习不再盲目 七、&#x1f381;更多干货 一、&#x1f468;‍&#x1f…...

MySQL 知识小结(一)

一、my.cnf配置详解 我们知道安装MySQL有两种方式来安装咱们的MySQL数据库&#xff0c;分别是二进制安装编译数据库或者使用三方yum来进行安装,第三方yum的安装相对于二进制压缩包的安装更快捷&#xff0c;但是文件存放起来数据比较冗余&#xff0c;用二进制能够更好管理咱们M…...

比较数据迁移后MySQL数据库和OceanBase数据仓库中的表

设计一个MySQL数据库和OceanBase数据仓库的表数据比较的详细程序流程,两张表是相同的结构,都有整型主键id字段,需要每次从数据库分批取得2000条数据,用于比较,比较操作的同时可以再取2000条数据,等上一次比较完成之后,开始比较,直到比较完所有的数据。比较操作需要比较…...

PostgreSQL——环境搭建

一、Linux # 安装 PostgreSQL 15 仓库 sudo dnf install -y https://download.postgresql.org/pub/repos/yum/reporpms/EL-$(rpm -E %{rhel})-x86_64/pgdg-redhat-repo-latest.noarch.rpm# 安装之前先确认是否已经存在PostgreSQL rpm -qa | grep postgres# 如果存在&#xff0…...