C++笔记:三种适配器(分别修饰函数、迭代器、容器)

Algorithms看不见Containers,对其一无所知。所以,它所需要的一切信息都必须从iterators取得,而iterators(由Containers提供)必须能够回答Algorithm的所有提问,才能搭配该Algorithm的所有操作。

1. C++ 标准库提供了五种迭代器类别

  1. Input Iterator(输入迭代器)
  2. Output Iterator(输出迭代器)
  3. Forward Iterator(前向迭代器)
  4. Bidirectional Iterator(双向迭代器)
  5. Random Access Iterator(随机访问迭代器)

2.iterator模版

std::iterator 是一个方便的模板,它简化了自定义迭代器的定义。它提供了标准化的方式来定义迭代器的类型信息。它有以下五个模板参数:

  1. iterator_category迭代器的类别(如 input_iterator_tagoutput_iterator_tag 等)。
  2. value_type迭代器所指向的值的类型。
  3. difference_type两个迭代器之间的距离类型。
  4. pointer指向 value_type 的指针类型。
  5. reference引用 value_type 的类型。

3.各种容器的iterators的iterator_category

#include<typeinfo>void _display_category(input_iterator_tag) {std::cout << "Input iterator" << std::endl;
}void _display_category(output_iterator_tag) {std::cout << "Output iterator" << std::endl;
}void _display_category(forward_iterator_tag) {std::cout << "Forward iterator" << std::endl;
}void _display_category(bidirectional_iterator_tag) {std::cout << "Bidirectional iterator" << std::endl;
}  // √void _display_category(random_access_iterator_tag) {std::cout << "Random access iterator" << std::endl;
}void _display_category(std::input_iterator_tag) {std::cout << "Input iterator" << std::endl;
}  // 输入迭代器void _display_category(std::output_iterator_tag) {std::cout << "Output iterator" << std::endl;
}  // 输出迭代器template<typename I>
void display_category(I it)
{// 提问与回答typename iterator_traits<I>::iterator_category cagy;_display_category(cagy);cout << "typeid(it).name()=" << **typeid**(it).name << endl;
}display_category(set<int>::iterator());
  • 使用了typeid(it).name()来输出迭代器it的类型信息
display_category(istream_iterator<int>());  // Input iterator
display_category(ostream_iterator<int>(cout, ""));  // Output iterator
  • 输入迭代器的定义: 
template <typename T, typename CharT = char, typename Traits = std::char_traits<CharT>, typename Dist = std::ptrdiff_t>
class istream_iterator{
public:typedef input_iterator_tag iterator_category;  // (1)typedef T  value_type;  // (2)typedef const T* pointer;  // (3)typedef const T& reference;  // (4)typedef Dist difference_type;  // (5)
private:basic_istream<CharT, Traits>* in_stream;T value;...
};

4.iterator_category对算法的影响

例如:distance算法求解两个指针之间的距离。

template<class InputIterator>
inline iterator_traits<InputIterator>::difference_type
distance(InputIterator first, InputIterator last)
{typedef typename iterator_traits<InputIterator>::iterator_category category;return __distance(first, last, catrgory());
}template<class InputIterator>
inline iterator_traits<InputIterator>::difference_type
__difference(InputIterator first, InputIterator last, input_iterator_tag)
{iterator_traits<InputIterator>::difference_type n=0;while(first != last){++first; ++n;}return n;
}template<class RandomAccessIterator>
inline iterator_traits<RandomAccessIterator>::difference_type
__difference(RandomAccessIterator first, RandomAccessIterator last, random_access_iterator_tag)
{return last - first;
}
  • inline关键字:建议编译器内联扩展此函数,尽量避免函数调用的开销,具体是否内联由编译器决定。
  • typename iterator_traits<InputIterator>::difference_type:这是函数的返回类型。

random_access_iterator_tag → bidirectional_iterator_tag → farward_iterator_tag → input_iterator_tag

!如果当萃取机问得的答案category是farward_iterator_tag 或 bidirectional_iterator_tag时,要调用 input_iterator_tag 的实现。

 

  • 算法源码中对iterator_category的“暗示”
    • 对于输入迭代器(input_iterator_tag),使用逐元素遍历计数的方式计算距离。
    • 对于随机访问迭代器(random_access_iterator_tag),使用指针减法直接计算距离。

5.accumulate算法内部剖析

通常有两种版本

第一种,默认版本:

template <class InputIterator, class T>
T accumulate(InputIterator first, InputIterator last, T init)
{for( ; first != last; ++first)init = init + *first;return init;
}

第二种,加了仿函数:

template <class InputIterator, class T>
T accumulate(InputIterator first, InputIterator last, T init, BinaryOperation binary_op)
{for(;first!=last;++first)init = binary_op(init, *first);return init;
}

调用:

int myfunc(int x, int y)
{ return x + 2*y;}struct myclass
{int operator()(int x, int y){ return x + 3*y;}
} myobj;
void test_accumulate()
{int init = 100;int nums[] = {10, 20, 30};accumulate(nums, nums+3, init);  // 160accumulate(nums, nums+3, init, minus<int>());  // 40accumulate(nums, nums+3, init, myfunc);  // 220 仿函数accumulate(nums, nums+3, init, myobj);  // 280  函数对象
}

6.容器中是否带有全局算法的同名成员函数

count()

  • 带:set/multiset, map/multimap, unordered_set/unordered_multiset, unordered_map/unordered_multimap
  • 不带:array, vector, list, forward_list, deque

find()

  • 带:set/multiset, map/multimap, unordered_set/unordered_multiset, unordered_map/unordered_multimap
  • 不带:array, vector, list, forward_list, deque

sort()

  • 带:list, forward_list
  • 不带:array, vector, deque, set/multiset, map/multimap, unordered_set/unordered_multiset, unordered_map/unordered_multimap

7.仿函数functors的可适配adaptable条件

// using default comparison (operator <):
sort(myvec.begin(), myvec.end());// 没有融入STL(因为没有继承,无法回答问题)
sort(myvec.begin(), myvec.end(), myfunc);
sort(myvec.begin(), myvec.end(), myobj);// 融入STL
sort(myvec.begin(), myvec.end(), less<int>());
sort(myvec.begin(), myvec.end(), greater<int>());
int myfunc(int x, int y)
{ return (x < y);}struct myclass
{int operator()(int x, int y){ return (x < y);}
} myobj;template<class T>
struct greater : public binary_function<T,T,bool>{bool operator()(const T& x, const T& y){return x > y;}
};template<class T>
struct less : public binary_function<T,T,bool>{bool operator()(const T& x, const T& y){return x < y;}
};
  • 通过继承自binary_functionless与greayer自动获得了这些类型定义。
typedef int first_argument_type;
typedef int second_argument_type;
typedef bool result_type;
  • 一个仿函数要适配STL中的函数适配器(能够回答问题),通常需要满足以下条件:
    1. 类型定义:提供first_argument_typesecond_argument_typeresult_type这些类型定义(对于二元仿函数)。
    2. 操作符重载:重载函数调用操作符operator(),实现实际的操作逻辑。
  • STL提供了一些仿函数适配器,用于创建或修改仿函数以满足特定的需求

 

8.常见适配器

  • 容器适配器(内含一个容器)stack,queue
template <class T, class Sequence=deque<T>>
class stack {
...
public:typedef typename Sequence::value_type value_type;typedef typename Sequence::size_type size_type;typedef typename Sequence::reference reference;typedef typename Sequence::const_reference const_reference;
protected:Sequence c; // deque是stack的底层容器
public:bool empty() const { return c.empty(); }size_type size() const { return c.size(); }reference top() { return c.back(); }const_reference top() const { return c.back(); }void push(const value_type& x) { c.push_back(x); }void pop() { c.pop_back(); )
  • 仿函数适配器(将一个函数对象转换成另一个函数对象):binder2nd

bind2nd 是一个函数适配器,用于将二元函数对象的第二个参数绑定为一个固定值,从而生成一个新的仿函数,使其只接受一个参数。

auto less_than_5 = bind(less<int>(), 5);
  • bind2nd 函数模板通过绑定二元函数对象的第二个参数,返回一个 binder2nd 对象。

    template <class Operation, class T>
    inline binder2nd<Operation> bind2nd(const Operation& op, const T& x) {typedef typename Operation::second_argument_type arg2_type;return binder2nd<Operation>(op, arg2_type(x));
    }
    
  • binder2nd 类模板继承自 unary_function,实现将二元函数对象转换为一元函数对象。

    template <class Operation>
    class binder2nd : public unary_function<typename Operation::first_argument_type, typename Operation::result_type> {
    protected:Operation op; // 内部成员,存储二元函数对象typename Operation::second_argument_type value; // 存储绑定的第二个参数
    public:// 构造函数binder2nd(const Operation& x, const typename Operation::second_argument_type& y) : op(x), value(y) {}// 重载的函数调用操作符typename Operation::result_type operator()(const typename Operation::first_argument_type& x) const {return op(x, value); // 实际调用存储的二元函数对象,并将value作为第二个参数}
    };
    
  • 新型适配器bind

    std::bind 是一个函数适配器,可以将函数或函数对象的一部分参数绑定到特定的值上,从而生成一个新的函数对象。它可以绑定:

    1. 普通函数
    2. 函数对象
    using namespace std::placeholders;
    double my_divide(double x, double y)
    { return x / y;}auto fn_five = bind(my_divide, 10, 2);
    cout << fn_five() << endl;  // 5auto fn_half = bind(my_divide, _1, 2);  // 占位符
    cout << fn_five(10) << endl;  // 5auto fn_invert = bind(my_divide, _2, _1);  // 占位符
    cout << fn_five(10, 2) << endl;  // 0.2auto fn_rounding = bind<int>(my_divide, _1, _2);  // 占位符
    cout << fn_five(10, 3) << endl;  // 3
    
    1. 成员函数
    struct MyPair {double a, b;double multiply() {return a * b;}
    };MyPair ten_two {10, 2};
    auto bound_memfn = bind(&MyPair::multiply, _1);
    cout << bound_memfn(ten_two) << endl;  // 20
    
    1. 数据成员
    auto bound_memdata = bind(&MyPair::a, ten_two);
    cout << bound_memdata() << endl;  // 绑定a,输出:10auto bound_memdata2 = bind(&MyPair::b, std::placeholders::_1);
    cout << bound_memdata2(ten_two) << endl;  // 绑定b,输出:2
    
  • 迭代器适配器reverse_iterator

    • 使用 reverse_iterator 适配器将普通的前向迭代器转换为反向迭代器,使得可以从容器的末尾向前遍历元素。

    • rbegin(): 返回一个指向容器最后一个元素的反向迭代器

      reverse_iterator rbegin()
      {return reverse_iterator(end());}
      
    • rend(): 返回一个指向容器第一个元素前一个位置的反向迭代器。

      reverse_iterator rend()
      {return reverse_iterator(begin());}
      
    template <class Iterator>
    class reverse_iterator
    {
    protected:Iterator current; // 对应的正向迭代器
    public:typedef typename iterator_traits<Iterator>::iterator_category iterator_category;typedef typename iteratot_traits<Iterator>::value_type value_type;typedef typename iteratot_traits<Iterator>::difference_type difference_type;typedef typename iteratot_traits<Iterator>::pointer pointer;typedef typename iteratot_traits<Iterator>::reference reference;typedef Iterator iterator_type;  // 代表正向迭代器typedef reverse_iterator<Iterator> self;  // 代表逆向迭代器
    public:// 构造函数:将传入的迭代器 x 用于初始化成员变量 current// 该构造函数用于将一个正向迭代器转换为一个反向迭代器explicit reverse_iterator(iterator_type x): current(x){}//复制一个现有的反向迭代器对象reverse_iterator(const self& x): current(x.current) {}// 取出对应的正向迭代器iterator_type base() const{ return current;}// 逆向迭代器取值时,对应的正向迭代器向后移一位reference operator*() const { Iterator tmp = current; return *--tmp;}pointer operator->() const{ return &operator*();}self& operator++ { --current; return *this;}self& operator-- { ++current; return *this;}self operator+(difference_type n) { return self(current - n);}self operator-(difference_type n) { return self(current + n);}
    };
    
  • 迭代器适配器inserter

 

list<int> foo, bar;
for(int i=1; i<5; i++)
{ foo.push_back(i); bar.push_back(i*10);}list<int>::iterator it = foo.begin();
advance(it, 3);copy(bar.begin(), bar.end(), inserter(foo,it));
// inserter中的container是指向foo的指针
template <class InputIterator, class OutputIterator>
OutputIterator copy(InputIterator first, InputIterator last, OutputIterator result)
{while(first!=last){*result = *first;++first;++result;}return result;
}
  • *result = *first这一步,输出迭代器需要重载等号操作符是因为对于标准的迭代器(如指针、vector的迭代器等),这些操作符已经默认提供。然而,对于自定义迭代器(如插入迭代器),需要重载这些操作符以实现特定的行为。
template <class Container, class Iterator>
inline insert_iterator<Container> insert(Container& x, Iterator i)
{typedef typename Container::iterator iter;  // 容器的迭代器的类型//  将传入的迭代器类型转换为容器的迭代器的类型return insert_iteraor<Container>(x, iter(i));
};
  • Container& x:这是一个引用,指向将要进行插入操作的容器。
  • Iterator i:这是一个迭代器,指示插入位置。
tempalte <class Container>
class insert_iterator
{
protected:Container* container;  // 底层容器的指针typename Container::iterator iter;typedef insert_iterator<Container> self;
public:typedef output_iterator_tag iterator_category;// 初始化时,将容器地址赋给containerinsert_iterator(Container& x, typename Container::iterator i):container(&x), iter(i) {}self& operaor=(const typename Container::value_type& value){iter = container->insert(iter,value);  // 使用容器的insert方法插入value++iter;   // 插入后,迭代器前移return *this;}
};
  • ++iter; 令insert iterator永远随其target贴身移动

    举例:

    int main(){vector<int> v = {1, 2, 3};vector<int>::iterator it = v.begin();advance(it, 1);insert_iterator<vector<int>> inserter(v, it);*inserter = 10;  // 插入10后,inserter迭代器自动更新到10之后*inserter = 20;  // 插入20后,inserter迭代器自动更新到20之后// 1, 10, 20, 2, 3
    
  • 创建一个插入迭代器insert_iterator 对象有两种方法

    • 直接调用构造函数
    insert_iterator<vector<int>> inserter(v, it);
    // 这里 inserter 是一个 insert_iterator<vector<int>> 对象
    // 这直接调用了 insert_iterator 的构造函数
    
    • 使用辅助函数inserter
    auto inserter_it = inserter(v, it);
    // 这里 inserter_it 是一个 insert_iterator<vector<int>> 对象
    // inserter(v, it) 调用了辅助函数 inserter
    // 该函数内部调用了 insert_iterator 的构造函数
    // 创建并返回一个 insert_iterator 对象
    

9.输出流迭代器ostream_iterator

  • 代码示例
int main(){vector<int> v;for(int i=1; i<10; i++){v.push_back(i*10);}ostream_iterator<int> out_it(cout, ", ");copy(v.begin(), v.end(), out_it);
template <class InputIterator, class OutputIterator>
OutputIterator copy(InputIterator first, InputIterator last, OutputIterator result)
{while(first!=last){*result = *first;++first;++result;}return result;
}
  • 因为iterator中有五个模板参数。而对于 ostream_iterator,它是一个输出迭代器,因此只关心 iterator_category,而其他类型都不需要(可以设置为 void)。
template <class T, class charT=char, class traits=char_traits<charT>>
class ostream_iterator:public iterator<output_iterator_tag, void, void, void, void>
{
protected:basic_ostream<charT, traits>* out_stream;const charT* delim;
public:typedef ostream_iterator<T, charT, traits> self;typedef basic_ostream<charT, traits> ostream_type;// ostream_iterator<int> out_it(cout);ostream_iterator(ostream_type& s):out_stream(&s), delim(0) {}// ostream_iterator<int> out_it(cout, ", ");ostream_iterator(ostream_type& s, const charT* delimiter) : out_stream(&s), delim(delimiter) {}// ostream_iterator<int> out_it(cout, ", ");// ostream_iterator<int> out_it_copy(out_it);ostream_iterator(const ostream_iterator<T, charT, traits>& x) : out_stream(x.out_stream), delim(x.delim) {}~ostream_iterator() {}self& operator=(const T& value) {*out_stream << value;  // out_stream 指向的输出流对象是coutif (delim != 0) *out_stream << delim;return *this;}

10.输入流迭代器istream_iterator

#include <iostream>    
#include <iterator>     int main() {double value1, value2;cout << "Please, insert two values: ";istream_iterator<double> eos;        // end-of-stream iterator 表示输入流的结束istream_iterator<double> iit(cin); // stdin iterator 绑定到标准输入 std::cinif (iit != eos) value1 = *iit; // 读取第一个值,因为在构造时就已经开始尝试从输入流中读取一个值++iit; // 移动到下一个输入if (iit != eos) value2 = *iit; // 读取第二个值cout << value1 << " * " << value2 << " = " << (value1 * value2) << '\\n';return 0;
}
template <class T, class charT=char, class traits=char_traits<charT>, class Distance=ptrdiff_t>
class istream_iterator : public iterator<input_iterator_tag, T, Distance, const T*, const T&> {basic_istream<charT, traits>* in_stream; // 输入流指针T value; // 存储读取的值public:typedef basic_istream<charT, traits> istream_type;typedef istream_iterator<T, charT, traits, Distance> self;istream_iterator() : in_stream(0) {}  // 作为结束迭代器的标志istream_iterator(istream_type& s) : in_stream(&s) { ++*this; }  // cin就是输入流指针sistream_iterator(const self& x) : in_stream(x.in_stream), value(x.value) {}~istream_iterator() {}const T& operator*() const { return value; }const T* operator->() const { return &value; }self& operator++() {if (in_stream && !(*in_stream >> value)) in_stream = 0;return *this;}self operator++(int) {istream_iterator<T, charT, traits, Distance> tmp = *this;++*this;return tmp;}bool operator==(const self& rhs) const {return in_stream == rhs.in_stream;}bool operator!=(const self& rhs) const {return in_stream != rhs.in_stream;}
};

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.rhkb.cn/news/332249.html

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

国内首个智能体生态大会!2024百度万象大会定档5月30日

最近&#xff0c;百度悄悄「上新」了几个AI神器。 百度搜索上线「互动」功能&#xff0c;可以实时问答&#xff0c;查询信息就像聊天一样简单&#xff0c;还可以艾特相关智能体&#xff0c;更细致精确地满足个性化需求&#xff0c;比如去新加坡旅游&#xff0c;可以让新加坡旅…

智慧校园学工管理系统的部署

学工体系思政服务该怎么规划建造&#xff1f;思政作为高校育人的中心使命&#xff0c;在做到让学生健康高兴生长的一起&#xff0c;也应满意学生生长成才的各类需求。使用技术为学生供给优质的信息化服务&#xff0c;是其间的有效途径。大数据让个性化教育成为可能&#xff0c;…

OTA在线旅行社系统架构:连接世界的科技纽带

随着互联网的快速发展和人们对旅行需求的不断增长&#xff0c;OTA&#xff08;Online Travel Agency&#xff09;在线旅行社成为了现代旅行业中的重要一环。OTA系统架构的设计和实现将对旅行行业产生深远影响。本文将探讨OTA在线旅行社系统架构的重要性和关键组成部分&#xff…

人工智能为犯罪地下世界带来了巨大的生产力提升

每周跟踪AI热点新闻动向和震撼发展 想要探索生成式人工智能的前沿进展吗&#xff1f;订阅我们的简报&#xff0c;深入解析最新的技术突破、实际应用案例和未来的趋势。与全球数同行一同&#xff0c;从行业内部的深度分析和实用指南中受益。不要错过这个机会&#xff0c;成为AI领…

centos7.9用docker运行一个nginx容器

首先你的linux 系统里面已经安装好了docker&#xff0c;docker的安装教程看这个 1&#xff0c;下载nginx镜像 有很多文章会把镜像下载说成是拉取镜像&#xff0c; 我觉得就是下载的意思啊&#xff0c;搞不懂为什么要说拉取&#xff1f; docker pull nginx 下载最新版 Nginx …

开源的在线JSON数据可视化编辑器jsoncrack本地部署与远程访问

文章目录 1. 在Linux上使用Docker安装JSONCrack2. 安装Cpolar内网穿透工具3. 配置JSON Crack界面公网地址4. 远程访问 JSONCrack 界面5. 固定 JSONCrack公网地址 JSON Crack 是一款免费的开源数据可视化应用程序&#xff0c;能够将 JSON、YAML、XML、CSV 等数据格式可视化为交互…

【移动云】云端赋能——数字化时代游戏与工作的新境界

前言 在当今这个信息化、数字化的时代&#xff0c;云计算、大数据和人工智能等前沿技术已经深入到我们生活的方方面面。作为我国通信行业的领军企业&#xff0c;中国移动凭借其在5G技术领域的领先优势&#xff0c;推出了基于移动云计算技术的云业务品牌——移动云。移动云以云操…

Linux 的性能调优的思路

Linux操作系统是一个开源产品&#xff0c;也是一个开源软件的实践和应用平台&#xff0c;在这个平台下有无数的开源软件支撑&#xff0c;我们常见的apache、tomcat、mysql等。 开源软件的最大理念是自由、开放&#xff0c;那么Linux作为一个开源平台&#xff0c;最终要实现的是…

Overleaf中出现文字越界、越下届、没有正确分页、换页的原因和解决方法

在使用overleaf中&#xff0c;我偶尔会遇到如标题所说的情况&#xff0c;也如图所示&#xff1a; 后来发现&#xff0c;是因为这一页前面是一个表格&#xff0c;所以怀疑是表格的格式导致的。所以让chatgpt帮我更换了表格的格式&#xff0c;成功解决问题。 对于问题可能的成因…

<Python实际应用>用yolov9实现垃圾检测

公司一个项目需要在无人机巡检的画面中识别垃圾和汽车&#xff0c;正好听闻yolov9最新出炉&#xff0c;于是试了一下采用yolov9来搭建该项目 1.下载和部署 下载yolov9:GitHub地址&#xff1a;GitHub代码下载地址 配置环境可以参考之前关于yolov5的文章 Yolov5自学笔记之一-…

【二叉树】LeetCode.144:二叉树的前序遍历(小细节把握)

&#x1f381;个人主页&#xff1a;我们的五年 &#x1f50d;系列专栏&#xff1a;初阶初阶结构刷题 &#x1f389;欢迎大家点赞&#x1f44d;评论&#x1f4dd;收藏⭐文章 目录 1.题目描述&#xff1a;​编辑 2.问题分析&#xff1a; &#x1f354;函数解读&#xff1a; …

Linux系统启动原理

Linux系统启动原理及故障排除 Centos6系统启动过程 修改系统启动级别 vim /etc/inittabCentos7启动流程 加载BIOS信息&#xff0c;进行硬件检测 根据BIOS设定读取设备中的MBR&#xff0c;加载Boot loader 加载内核&#xff0c;内核初始化以后以模块的形式动态加载硬件 并且加…

【Spring】深入理解 Spring 状态机:简化复杂业务逻辑的利器

前言 在软件开发中&#xff0c;有许多场景需要处理状态转换和状态驱动的逻辑&#xff0c;比如订单处理、工作流程管理、游戏引擎等。Spring 状态机&#xff08;Spring State Machine&#xff09;是 Spring Framework 提供的一个强大的模块&#xff0c;用于帮助开发人员轻松构建…

IOT技术怎么落地?以宝马,施耐德为例

物联网技术 物联网&#xff08;IoT&#xff09;技术正逐渐成为数字化工厂转型的核心驱动力。本文将通过实际案例&#xff0c;探讨IoT技术如何促进制造业的数字化转型&#xff0c;提高生产效率&#xff0c;降低成本&#xff0c;并提升产品质量。 1. 物联网技术简介 物联网技术通…

【Python】 XGBoost模型的使用案例及原理解析

原谅把你带走的雨天 在渐渐模糊的窗前 每个人最后都要说再见 原谅被你带走的永远 微笑着容易过一天 也许是我已经 老了一点 那些日子你会不会舍不得 思念就像关不紧的门 空气里有幸福的灰尘 否则为何闭上眼睛的时候 又全都想起了 谁都别说 让我一个人躲一躲 你的承诺 我竟然没怀…

部署LAMP平台

目录 一、LAMP简介与概述 1.1 各组件作用 1.2 LAMP平台搭建时各组件安装顺序 1.3 httpd服务的目录结构 1.4 httpd.conf配置文件 二、编译安装Apache httpd服务 2.1 关闭防火墙&#xff0c;将安装Apache所需软件包传到/opt目录下 2.2 安装环境依赖包 ​2.3 配置软件模块…

3.Redis之Redis的环境搭建redis客户端介绍

1.版本的选取 安装 Redis&#xff1a;Redis 5 系列~~ 在 Linux 中进行安装~~ Redis 官方是不支持 Windows 版本的~~ 微软维护了一个 Windows 版本的 Redis 分支 Centos和Ubuntu.Docker 2.如何进行安装&#xff1f;&#xff1f;&#xff1f; 1.ubuntu 2.centos yum instal…

设计模式在芯片验证中的应用——模板方法

一、模板方法 模板方法(Template Method)设计模式是一种行为设计模式&#xff0c; 它在父类中定义了一个功能的框架&#xff0c; 允许子类在不修改结构的情况下重写功能的特定步骤。也就是模板方法定义了一组有序执行的操作&#xff0c;将一些步骤的实现留给子类&#xff0c;同…

【C语言】二叉树的实现

文章目录 前言⭐一、二叉树的定义&#x1f6b2;二、创建二叉树&#x1f3a1;三、二叉树的销毁&#x1f389;四、遍历二叉树1. 前序遍历2. 中序遍历3. 后序遍历4. 层序遍历 &#x1f332;五、二叉树的计算1. 计算二叉树结点个数2. 计算二叉树叶子结点的个数3. 计算二叉树的深度4…

Go语言之GORM框架(二) ——GORM的单表操作

前言 在上一篇文章中&#xff0c;我们对Gorm进行了介绍&#xff0c;而在这一篇文章中我们主要介绍GORM的单表查询与Hook函数,在进行今天的内容之前我们先事先说明一下&#xff0c;下面我们对单表进行操作的表结构如下&#xff1a; type Student struct {ID uint gorm:&qu…