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

【C++】STL——list的模拟实现、构造函数、迭代器类的实现、运算符重载、增删查改

文章目录

  • 1.模拟实现list
    • 1.1构造函数
    • 1.2迭代器类的实现
    • 1.3运算符重载
    • 1.4增删查改

1.模拟实现list

list使用文章

在这里插入图片描述

1.1构造函数

在这里插入图片描述

析构函数

在这里插入图片描述

  在定义了一个类模板list时。我们让该类模板包含了一个内部结构体_list_node,用于表示链表的节点。该结构体包含了指向前一个节点和后一个节点的指针以及节点的值。在list中保存了链表的头节点指针和链表长度大小。

namespace my_list
{	//用类模板定义一个list结点template<class T>struct _list_node{//list结点的构造函数,T()作为缺省值,当未转参时调用_list_node(const T& val = T()):_next(nullptr), _prev(nullptr), _val(val){}//list_node的成员函数,list为带头双向循环链表_list_node* _prev;_list_node* _next;T _val;};//用类模拟实现listtemplate<class T>class list{private:typedef _list_node<T> Node;public://list的构造函数,初始化头节点指针list(){_head = new Node;_head->_prev = _head;_head->_next = _head;_size = 0;}//析构函数~list(){iterator it = begin();while (it != end()){it = erase(it);}_size = 0;delete _head;_head = nullptr;}private://成员函数为list的头节点指针,和链表长度Node* _head;size_t _size;};
}

1.2迭代器类的实现

在这里插入图片描述
在这里插入图片描述

  因为list的底层实现和之前的vector和string不同,vector和string是数组和顺序表,而list底层是链表。而对于数组和顺序表,我们可以直接使用指针来实现其迭代器,但是对于list类型就无法使用指针来实现它的迭代器,因为迭代器++无法访问到他的下一个节点。所以我们这里建立一个迭代器的结构体,用来封装node指针,来实现list的专属迭代器

  这段代码定义了一个名为__list_iterator的迭代器结构体,用于遍历链表。该结构体模板有三个模板参数:T表示链表中节点的数据类型,Ref表示引用类型T&,Ptr表示指针类型 T* _node:指向链表节点的指针。构造函数__list_iterator(Node* node):用于初始化迭代器对象,将指针node赋值给_node。通过_node就可以实现各种操作符重载。

  定义了一个迭代器结构体,用于在链表中进行遍历和操作。通过重载运算符,可以使用迭代器对象像指针一样访问链表中的元素。

//使用类进行封装Node*,来实现list的专属迭代器
//list迭代器和一些内置类型迭代器不一样,++无法准确访问到下一个位置
//typedef __list_iterator<T, T&> iterator;
//typedef __list_iterator<T, const T&> iterator;
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* node):_node(node){}//重载*Ref operator*(){return _node->_val;}//重载->Ptr operator->(){return &_node->_val;}//重载前置++self& operator++(){_node = _node->_next;return *this;}//重载后置++self operator++(int){__list_iterator<T> 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& it) const{return _node != it._node;}//重载==bool operator==(const self& it) const{return _node == it._node;}
};

  通过建立一个新的结构体来重载迭代器,我们即可实现list专属迭代器对其链表进行++、- -等操作。

typedef __list_iterator<T, T&, T*> iterator;
typedef __list_iterator<T, const T&, const T*> const_iterator;//list迭代器实现
iterator begin()
{//隐式类型转换,内置类型转换为自定义类型,实现迭代器功能//return _head->_next;return iterator(_head->_next);
}iterator end()
{//return _head;return iterator(_head);
}const_iterator begin() const
{//return _head->_next;return const_iterator(_head->_next);
}const_iterator end() const
{//return _head;return const_iterator(_head);
}

1.3运算符重载

  迭代器的运算符重载在上面的代码中,已经都基本实现了。

在这里插入图片描述

  赋值运算符重载函数operator=首先创建了一个临时的链表对象lt,并将传入的链表对象的副本赋值给lt。然后,调用swap函数,将当前链表对象和临时链表对象进行交换。这样做的原因是为了实现异常安全的赋值操作。通过将传入的链表对象的副本赋值给临时链表对象,可以确保在交换过程中不会影响到传入的链表对象。 最后,返回当前链表对象的引用。

  通过这样的实现逻辑,可以实现链表对象之间的赋值操作,并且保证在异常发生时不会破坏数据的完整性。

//交换
void swap(list<T>& lt)
{std::swap(_head, lt._head);std::swap(_size, lt._size);
}//赋值运算符重载
list<T>& operator=(list<T> lt)//list& operator=(list lt)
{swap(lt);return *this;
}

1.4增删查改

push_back

在这里插入图片描述
  和带头双向循环链表的尾插一样。首先,创建一个新的链表节点newnode,节点的值为传入的参数x。然后,获取到链表的尾指针tail,即头节点的前一个节点。

  接着,将新节点newnode插入到链表的尾部。首先,将新节点的前驱指针指向尾节点tail,将尾节点的后继指针指向新节点。这样就将新节点连接到了链表的尾部。

  然后,将新节点的后继指针指向头节点,将头节点的前驱指针指向新节点。这样就将新节点连接到了链表的头部。

//尾插
void push_back(const T& x)
{//创建一个新的链表结点,且获取到链表的尾指针Node* newnode = new Node(x);Node* tail = _head->_prev;//连接尾结点newnode->_prev = tail;tail->_next = newnode;//连接头节点_head->_prev = newnode;newnode->_next = _head;++_size;
}

insert

在这里插入图片描述

  和上面的逻辑类似。

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

erase

在这里插入图片描述
  首先,通过断言判断传入的迭代器pos不等于链表的末尾迭代器。如果等于末尾迭代器,表示要删除的节点不存在,会导致错误。然后,根据传入的迭代器pos获取到要删除的节点cur,以及其前驱节点prev和后继节点next。

  接着,将前驱节点的后继指针指向后继节点,将后继节点的前驱指针指向前驱节点。这样就将要删除的节点从链表中断开。然后,释放要删除的节点的内存。还要将链表的大小减1。

  为了避免迭代器失效,返回下一个节点的指针作为新的迭代器。这样可以保证在删除节点后,仍然可以使用返回的迭代器进行遍历和操作。

//删除
iterator erase(iterator pos)
{assert(pos != end());Node* cur = pos._node;Node* prev = cur->_prev;Node* next = cur->_next;prev->_next = next;next->_prev = prev;delete cur;--_size;//为了避免迭代器失效,返回下一个结点指针return next;
}


完整实现

#pragma once#include<assert.h>namespace my_list
{	//用类模板定义一个list结点template<class T>struct _list_node{//list结点的构造函数,T()作为缺省值,当未转参时调用_list_node(const T& val = T()):_next(nullptr), _prev(nullptr), _val(val){}//list_node的成员函数,list为带头双向循环链表_list_node* _prev;_list_node* _next;T _val;};//使用类进行封装Node*,来实现list的专属迭代器//list迭代器和一些内置类型迭代器不一样,++无法准确访问到下一个位置//typedef __list_iterator<T, T&> iterator;//typedef __list_iterator<T, const T&> iterator;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* node):_node(node){}//重载*Ref operator*(){return _node->_val;}//重载->Ptr operator->(){return &_node->_val;}//重载前置++self& operator++(){_node = _node->_next;return *this;}//重载后置++self operator++(int){__list_iterator<T> 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& it) const{return _node != it._node;}//重载==bool operator==(const self& it) const{return _node == it._node;}};//实现const类型迭代器/*template<class T>struct __list_const_iterator{typedef list_node<T> Node;Node* _node;__list_const_iterator(Node* node):_node(node){}const T& operator*(){return _node->_val;}__list_const_iterator<T>& operator++(){_node = _node->_next;return *this;}__list_const_iterator<T> operator++(int){__list_const_iterator<T> tmp(*this);_node = _node->_next;return tmp;}bool operator!=(const __list_const_iterator<T>& it){return _node != it._node;}bool operator==(const __list_const_iterator<T>& it){return _node == it._node;}};*///用类模拟实现listtemplate<class T>class list{private:typedef _list_node<T> Node;public:typedef __list_iterator<T, T&, T*> iterator;typedef __list_iterator<T, const T&, const T*> const_iterator;//list迭代器实现iterator begin(){//隐式类型转换,内置类型转换为自定义类型,实现迭代器功能//return _head->_next;return iterator(_head->_next);}iterator end(){//return _head;return iterator(_head);}const_iterator begin() const{//return _head->_next;return const_iterator(_head->_next);}const_iterator end() const{//return _head;return const_iterator(_head);}//初始化void empty_init(){_head = new Node;_head->_prev = _head;_head->_next = _head;_size = 0;}//list的构造函数,初始化头节点指针list(){empty_init();}//拷贝构造list(const list<T>& lt)//list(const list& lt){empty_init();for (auto& e : lt){push_back(e);}}//交换void swap(list<T>& lt){std::swap(_head, lt._head);std::swap(_size, lt._size);}//赋值运算符重载list<T>& operator=(list<T> lt)//list& operator=(list lt){swap(lt);return *this;}//析构函数~list(){clear();delete _head;_head = nullptr;}//清理void clear(){iterator it = begin();while (it != end()){it = erase(it);}_size = 0;}//尾插void push_back(const T& x){//创建一个新的链表结点,且获取到链表的尾指针Node* newnode = new Node(x);Node* tail = _head->_prev;//连接尾结点newnode->_prev = tail;tail->_next = newnode;//连接头节点_head->_prev = newnode;newnode->_next = _head;++_size;//insert(end(), x);}//头插void push_front(const T& x){insert(begin(), x);}//尾删void pop_back(){erase(--end());}//头删void pop_front(){erase(begin());}//在pos位置之前插入iterator insert(iterator pos, const T& x){Node* cur = pos._node;Node* prev = cur->_prev;Node* newnode = new Node(x);prev->_next = newnode;newnode->_next = cur;cur->_prev = newnode;newnode->_prev = prev;++_size;return newnode;}//删除iterator erase(iterator pos){assert(pos != end());Node* cur = pos._node;Node* prev = cur->_prev;Node* next = cur->_next;prev->_next = next;next->_prev = prev;delete cur;--_size;//为了避免迭代器失效,返回下一个结点指针return next;}//返回sizesize_t size(){/*size_t sz = 0;iterator it = begin();while (it != end()){++sz;++it;}return sz;*/return _size;}private://成员函数为list的头节点指针,和链表长度Node* _head;size_t _size;};//打印函数void print(const list<int>& lt){list<int>::const_iterator it = lt.begin();while (it != lt.end()){// (*it) += 1;cout << *it << " ";++it;}cout << endl;}
}


测试代码

#define _CRT_SECURE_NO_WARNINGS 1#include<iostream>
#include<list>
using namespace std;#include"list.h"void test_list1()
{//list<int> lt;//lt.push_back(1);//lt.push_back(2);//lt.push_back(3);//lt.push_back(4);//for (auto e : lt)//{//	cout << e << " ";//}//cout << endl;my_list::list<int> lt;lt.push_back(1);lt.push_back(2);lt.push_back(3);lt.push_back(4);my_list::list<int>::iterator it = lt.begin();while (it != lt.end()){cout << *it << " ";++it;}cout << endl;auto itt = lt.begin();while (itt != lt.end()){++(*itt);++itt;}for (auto e: lt){cout << e << " ";}cout << endl;
}struct A
{A(int a1 = 0, int a2 = 0):_a1(a1), _a2(a2){}int _a1;int _a2;
};void test_list2()
{my_list::list<A> lt;lt.push_back(A(1, 1));lt.push_back(A(2, 2));lt.push_back(A(3, 3));lt.push_back(A(4, 4));auto it = lt.begin();while (it != lt.end()){//cout << *(it)._a1 << " " << *(it)._a2 <<endl;cout << it->_a1 << " " << it->_a2 << endl;++it;}cout << endl;
}void test_list3()
{my_list::list<int> lt;lt.push_back(1);lt.push_back(2);lt.push_back(3);lt.push_back(4);for (auto e : lt){cout << e << " ";}cout << endl;lt.push_front(5);lt.push_front(6);print(lt);lt.pop_front();lt.pop_back();print(lt);lt.clear();lt.push_back(10);lt.push_back(20);lt.push_back(30);lt.push_back(40);print(lt);cout << lt.size() << endl;}int main()
{//test_list1();//test_list2();test_list3();return 0;
}

相关文章:

【C++】STL——list的模拟实现、构造函数、迭代器类的实现、运算符重载、增删查改

文章目录 1.模拟实现list1.1构造函数1.2迭代器类的实现1.3运算符重载1.4增删查改 1.模拟实现list list使用文章 1.1构造函数 析构函数 在定义了一个类模板list时。我们让该类模板包含了一个内部结构体_list_node&#xff0c;用于表示链表的节点。该结构体包含了指向前一个节点…...

vscode 插件::EIDE

最新最全 VSCODE 插件推荐&#xff08;2023版&#xff09;_vscode_白墨石-华为云开发者联盟 (csdn.net) 超好用的开发工具-VScode插件EIDE_vscode eide_桃成蹊2.0的博客-CSDN博客 Setup | Embedded IDE For VSCode (em-ide.com)...

Python 网络编程

Python 网络编程 Python 提供了两个级别访问的网络服务&#xff1a; 低级别的网络服务支持基本的 Socket&#xff0c;它提供了标准的 BSD Sockets API&#xff0c;可以访问底层操作系统 Socket 接口的全部方法。高级别的网络服务模块 SocketServer&#xff0c; 它提供了服务器…...

SQL 数据科学:了解和利用联接

推荐&#xff1a;使用 NSDT场景编辑器助你快速搭建可编辑的3D应用场景 什么是 SQL 中的连接&#xff1f; SQL 联接允许您基于公共列合并来自多个数据库表的数据。这样&#xff0c;您就可以将信息合并在一起&#xff0c;并在相关数据集之间创建有意义的连接。 SQL 中的连接类型…...

(统计学习方法|李航)第五章决策树——四五节:决策树的剪枝,CART算法

目录 一&#xff0c;决策数的剪枝 二&#xff0c;CART算法 1.CART生成 &#xff08;1&#xff09;回归树的生成 &#xff08;2&#xff09;分类树的生成 2.CART剪枝 &#xff08;1&#xff09;剪枝&#xff0c;形成一个子树序列 &#xff08;2&#xff09;在剪枝得到的子…...

C语言--结构体定义

整型数&#xff0c;浮点数&#xff0c;字符串是分散的数据表示&#xff0c;有时候我们需要很多类型表示一个整体&#xff0c;比如学生信息。 数组是元素类型一样的数据集合&#xff0c;如果是元素类型不同的数据集合&#xff0c;就要用到结构体 结构体一般是个模板&#xff0c;…...

解决Element Plus中Select在El Dialog里层级过低的问题(修改select选项框样式)

Element Plus是Vue.js的一套基于Element UI的组件库&#xff0c;提供了丰富的组件用于构建现代化的Web应用程序。其中&#xff0c;<el-select>是一个常用的下拉选择器组件&#xff0c;但在某些情况下&#xff0c;当<el-select>组件嵌套在<el-dialog>&#xf…...

【数据结构】二叉树 链式结构的相关问题

本篇文章来详细介绍一下二叉树链式结构经常使用的相关函数&#xff0c;以及相关的的OJ题。 目录 1.前置说明 2.二叉树的遍历 2.1 前序、中序以及后序遍历 2.2 层次遍历 3.节点个数相关函数实现 3.1 二叉树节点个数 3.2 二叉树叶子节点个数 3.3 二叉树第k层节点个数 3…...

【无标题】云原生在工业互联网的落地及好处!

什么是工业互联网&#xff1f; 工业互联网&#xff08;Industrial Internet&#xff09;是新一代信息通信技术与工业经济深度融合的新型基础设施、应用模式和工业生态&#xff0c;通过对人、机、物、系统等的全面连接&#xff0c;构建起覆盖全产业链、全价值链的全新制造和服务…...

人工智能在心电信号分类中的应用

目录 1 引言 2 传统机器学习中的特征提取与选择 3 深度学习中的特征提取与选择...

【Linux 网络】网络层协议之IP协议

IP协议 IP协议所处的位置网络层要解决的问题IP协议格式分片与组装网段划分特殊的IP地址IP地址的数量限制私网IP地址和公网IP地址路由 IP协议所处的位置 IP指网际互连协议&#xff0c;Internet Protocol的缩写&#xff0c;是TCP/IP体系中的网络层协议。 网络层要解决的问题 网络…...

.meta 文件

.meta 文件的作用简单来说是建立 Unity 与资源之间的“桥梁”。 在游戏中引用一个游戏资源&#xff0c;Unity 并不是直接按照文件的路径或者名称&#xff0c;而是使用一个独一无二的 GUID 来指向工程里该资源文件。 这个 GUID 就是存储在 Unity 工程为每一个资源和文件…...

CRITICAL_SECTION 用法

#include <stdio.h> #include <windows.h> typedef RTL_CRITICAL_SECTION CRITICAL_SECTION; CRITICAL_SECTION g_cs; //声明关键段 // 共享资源 char g_cArray[10]; unsigned int g_Count 0; DWORD WINAPI ThreadProc10(LPVOID pParam) { // 进入临界区 …...

汇川运动控制产品故障排查

针对汇川伺服产品&#xff08;IS600/IS620&#xff09;的基本检测和一些出现频率较高的故障进行检测判断方法&#xff0c;适用于服务人员在现场排查/判断机器故障时&#xff0c;准确定位问题。 一、简单故障排查 注1&#xff1a;接线错误&#xff1a;1、UVW相序是否正确&#…...

【Groups】50 Matplotlib Visualizations, Python实现,源码可复现

详情请参考博客: Top 50 matplotlib Visualizations 因编译更新问题&#xff0c;本文将稍作更改&#xff0c;以便能够顺利运行。 1 Dendrogram 树状图根据给定的距离度量将相似的点组合在一起&#xff0c;并根据点的相似性将它们组织成树状的链接。 新建文件Dendrogram.py: …...

windows安装kafka配置SASL-PLAIN安全认证

目录 1.Windows安装zookeeper&#xff1a; 1.1下载zookeeper 1.2 解压之后如图二 1.3创建日志文件 1.4复制 “zoo_sample.cfg” 文件 1.5更改 “zoo.cfg” 配置 1.6新建zk_server_jaas.conf 1.7修改zkEnv.cmd 1.8导入相关jar 1.9以上配置就配好啦&#xff0c;接下来启…...

【Linux】五种IO模型

文章目录 1. IO基本概念2. 五种IO模型2.1 五个钓鱼的例子2.2 五种IO模型2.2.1 阻塞IO2.2.2 非阻塞IO2.2.3 信号驱动IO2.2.4 IO多路转接2.2.5 异步IO 1. IO基本概念 认识IO IO就是输入和输出&#xff0c;在冯诺依曼体系结构中&#xff0c;将数据从输入设备拷贝到内存就叫输入&am…...

SCT82A30DHKR_5.5V-100V Vin同步降压控制器

SCT82A30是一款100V电压模式控制同步降压控制器&#xff0c;具有线路前馈。40ns受控高压侧MOSFET的最小导通时间支持高转换比&#xff0c;实现从48V输入到低压轨的直接降压转换&#xff0c;降低了系统复杂性和解决方案成本。如果需要&#xff0c;在低至6V的输入电压下降期间&am…...

备忘录模式(C++)

定义 在不破坏封装性的前提下&#xff0c;捕获一-个对象的内部状态&#xff0c;并在该对象之外保存这个状态。这样以后就可以将该对象恢复到原先保存的状态。 应用场景 ➢在软件构建过程中&#xff0c;某些对象的状态在转换过程中&#xff0c;可能由于某种需要&#xff0c;要…...

二叉排序树(二叉查找树)

二叉排序树&#xff08;二叉查找树&#xff09;的性质&#xff1a; 若它的左子树不为空&#xff0c;则左子树上所有结点的值均小于它的根结点的值。若它的右子树不为空&#xff0c;则右子树上所有结点的值均大于它的根将诶点的值。它的左、右子树也分别为二叉排序树。 对二叉…...

【杂谈】-递归进化:人工智能的自我改进与监管挑战

递归进化&#xff1a;人工智能的自我改进与监管挑战 文章目录 递归进化&#xff1a;人工智能的自我改进与监管挑战1、自我改进型人工智能的崛起2、人工智能如何挑战人类监管&#xff1f;3、确保人工智能受控的策略4、人类在人工智能发展中的角色5、平衡自主性与控制力6、总结与…...

ubuntu搭建nfs服务centos挂载访问

在Ubuntu上设置NFS服务器 在Ubuntu上&#xff0c;你可以使用apt包管理器来安装NFS服务器。打开终端并运行&#xff1a; sudo apt update sudo apt install nfs-kernel-server创建共享目录 创建一个目录用于共享&#xff0c;例如/shared&#xff1a; sudo mkdir /shared sud…...

反射获取方法和属性

Java反射获取方法 在Java中&#xff0c;反射&#xff08;Reflection&#xff09;是一种强大的机制&#xff0c;允许程序在运行时访问和操作类的内部属性和方法。通过反射&#xff0c;可以动态地创建对象、调用方法、改变属性值&#xff0c;这在很多Java框架中如Spring和Hiberna…...

EtherNet/IP转DeviceNet协议网关详解

一&#xff0c;设备主要功能 疆鸿智能JH-DVN-EIP本产品是自主研发的一款EtherNet/IP从站功能的通讯网关。该产品主要功能是连接DeviceNet总线和EtherNet/IP网络&#xff0c;本网关连接到EtherNet/IP总线中做为从站使用&#xff0c;连接到DeviceNet总线中做为从站使用。 在自动…...

RNN避坑指南:从数学推导到LSTM/GRU工业级部署实战流程

本文较长&#xff0c;建议点赞收藏&#xff0c;以免遗失。更多AI大模型应用开发学习视频及资料&#xff0c;尽在聚客AI学院。 本文全面剖析RNN核心原理&#xff0c;深入讲解梯度消失/爆炸问题&#xff0c;并通过LSTM/GRU结构实现解决方案&#xff0c;提供时间序列预测和文本生成…...

Maven 概述、安装、配置、仓库、私服详解

目录 1、Maven 概述 1.1 Maven 的定义 1.2 Maven 解决的问题 1.3 Maven 的核心特性与优势 2、Maven 安装 2.1 下载 Maven 2.2 安装配置 Maven 2.3 测试安装 2.4 修改 Maven 本地仓库的默认路径 3、Maven 配置 3.1 配置本地仓库 3.2 配置 JDK 3.3 IDEA 配置本地 Ma…...

用机器学习破解新能源领域的“弃风”难题

音乐发烧友深有体会&#xff0c;玩音乐的本质就是玩电网。火电声音偏暖&#xff0c;水电偏冷&#xff0c;风电偏空旷。至于太阳能发的电&#xff0c;则略显朦胧和单薄。 不知你是否有感觉&#xff0c;近两年家里的音响声音越来越冷&#xff0c;听起来越来越单薄&#xff1f; —…...

在QWebEngineView上实现鼠标、触摸等事件捕获的解决方案

这个问题我看其他博主也写了&#xff0c;要么要会员、要么写的乱七八糟。这里我整理一下&#xff0c;把问题说清楚并且给出代码&#xff0c;拿去用就行&#xff0c;照着葫芦画瓢。 问题 在继承QWebEngineView后&#xff0c;重写mousePressEvent或event函数无法捕获鼠标按下事…...

深度学习之模型压缩三驾马车:模型剪枝、模型量化、知识蒸馏

一、引言 在深度学习中&#xff0c;我们训练出的神经网络往往非常庞大&#xff08;比如像 ResNet、YOLOv8、Vision Transformer&#xff09;&#xff0c;虽然精度很高&#xff0c;但“太重”了&#xff0c;运行起来很慢&#xff0c;占用内存大&#xff0c;不适合部署到手机、摄…...

如何配置一个sql server使得其它用户可以通过excel odbc获取数据

要让其他用户通过 Excel 使用 ODBC 连接到 SQL Server 获取数据&#xff0c;你需要完成以下配置步骤&#xff1a; ✅ 一、在 SQL Server 端配置&#xff08;服务器设置&#xff09; 1. 启用 TCP/IP 协议 打开 “SQL Server 配置管理器”。导航到&#xff1a;SQL Server 网络配…...