C++模板 - 提高编程

news2024/11/25 0:38:04

引言

本阶段主要针对C++泛型编程STL技术做详细的讲解,探讨C++更深层的使用

1 模板

1.1 模板的概念

模板就是建立通用的模具,大大提高复用性
例如生活中的模板:

一寸照片模板:

 模板的特点:

        模板不可以直接使用,它只是一个框架
        模板的通用并不是万能的

1.2 函数模板

C++另一种编程思想称为泛型编程,主要利用的技术就是模板
C++提供了两种模板机制:函数模板和类模板

1.2.1 函数模板语法

函数模板的使用:
        建立一个通用函数,其函数返回值类型和形参类型可以不具体制定,用一个虚拟的类型来表示。

语法:

        template<typename T>
        函数的声明或者定义

解释:
        template --声明创建模板
        typename -- 表明后面的符号是一种数据类型,可以用class代替
        T -- 通用的数据类型,名称可以替换,通常为大写字母

#include <iostream>
using namespace std;

//函数模板

//交换两个整型的函数
void swapInt(int& a, int& b)
{
    int temp = a;
    a = b;
    b = temp;
}
//交换两个浮点型的函数
void swapDouble(double& a, double& b)
{
    double temp = a;
    a = b;
    b = temp;
}
//函数模板
template <typename T> //声明一个函数模板,告诉编译器后面代码中紧跟着的T不会报错,T是一个通用的数据类型
void mySwap(T& a, T& b)
{
    T temp = a;
    a = b;
    b = temp;
}
void test01()
{
    //使用函数模板 两种
    // 1.自动类型推导
    int a = 10;
    int b = 20;
    mySwap(a, b);
// 2.显示指定类型
    mySwap<int>(a,b); //告诉编译器里的类型为int
    cout << "a=" << a << " b=" << b << endl;
    
}
int main()
{
    test01();
    //    int a = 10;
    //    int b = 20;
    //    swapInt(a, b);
    //    cout << "a=" << a << " b=" << b << endl;
    //    double c = 1.2;
    //    double d = 2.2;
    //    swapDouble(c, d);
    //    cout << "c=" << c << " d=" << d << endl;

    return 0;
}

1.2.2 函数模板的注意事项

注意事项:

        自动类型推导,必须推导出一致的数据类型T,才可以使用
        模板必须要确定出T的数据类型,才可以使用

#include <iostream>
using namespace std;

//函数模板
template <class T> //函数模板可以用typename,可以用class
void mySwap(T& a, T& b)
{
    T temp = a;
    a = b;
    b = temp;
}

template <class T>
void func()
{
    cout << "fun函数调用" << endl;
}

void test01()
{
    //注意事项
    // 1. 自动类型推导,必须推导出一致的数据类型T,才可以使用

    int a = 10;
    int b = 20;
    //    char c = 'c';
    //        mySwap(a,c); //错误,推导不出一致的数据类型

    mySwap(a, b); //正确
    cout << "a=" << a << endl;
    cout << "b=" << b << endl;

    //     2. 模板必须要确定出T的数据类型,才可以使用
    //    func(); //不确定函数的类型,不可以使用
    func<int>();
}
int main()
{
    test01();
    //    int a = 10;
    //    int b = 20;
    //    swapInt(a, b);
    //    cout << "a=" << a << " b=" << b << endl;
    //    double c = 1.2;
    //    double d = 2.2;
    //    swapDouble(c, d);
    //    cout << "c=" << c << " d=" << d << endl;

    return 0;
}

总结:
 使用函数模板时,必须确定出通用的数据类型T,并且能够推导出一致的类型

1.2.3 函数模板的案例

#include <iostream>
using namespace std;

//函数模板 案例
//实现通用的 对数据进行排序的函数
// 使用 选择排序
//测试 char int 数组

//交换函数模板
template <typename T>
void mySwap(T& a, T& b)
{
    T temp = a;
    a = b;
    b = temp;
}

//打印数组模板
template <typename T>
void printArray(T arr[], int len)
{
    for (int i = 0; i < len; i++) {
        cout << arr[i] << " ";
    }
    cout << endl;
}
//排序算法

template <typename T>
void myShort(T& array, int len)
{
    for (int i = 0; i < len; i++) {
        int max = i;
        for (int j = i + 1; j < len; j++) {
            if (array[j] > array[max]) {
                max = j;
            }
        }
        if (max != i) {
            //交换元素
            //            int temp = array[i];
            //            array[i] = array[max];
            //            array[max] = temp;
            mySwap(array[max], array[i]);
        }
    }
}
void test01()
{
    char chararr[] = "bacde";
    myShort(chararr, sizeof(chararr) / sizeof(char));
    printArray(chararr, sizeof(chararr) / sizeof(char));
    cout << sizeof(chararr) / sizeof(char) << endl;
    //测试int 数组
    int array[5] = { 3, 12, 1, 4, 5 };
    myShort(array, sizeof(array) / sizeof(array[0]));
    printArray(array, sizeof(array) / sizeof(int));
    cout << sizeof(array) / sizeof(int) << endl;
}
int main()
{
    test01();

    return 0;
}

 1.2.4 普通函数和函数模板的区别

#include <iostream>
using namespace std;

//普通函数和函数模板的区别
// 1. 普通函数调用时可以发生 隐士类型转换
// 2. 函数模板 用自动类型推导 不会隐士类型转换
// 3. 函数模板 用显示指定类型,会发隐士类型转换
//普通函数
int myAdd01(int a, int b)
{
    return a + b;
}
template <typename T>
int myAdd02(T a, T b)
{
    return a + b;
}

void test01()
{
    int a = 10;
    int b = 20;
    char c = 'c'; // a - 97 c - 99
    cout << myAdd01(a, c) << endl; //可以调用,发送了隐式类型转换

    //使用模板调用
    //        cout << myAdd02(a, c) << endl; //自动推导,没有隐式类型转换
    cout << myAdd02<int>(a, c) << endl; //显示指定类型,有隐式类型转换
}
int main()
{
    test01();

    return 0;
}

 1.2.5 普通函数和函数模板的调用规则

#include <iostream>
using namespace std;

//普通函数和函数模板的调用规则
// 1.如果函数模板和普通函数都可以调用,优先调用普通函数
// 2.可以通过空模板参数列表强制调用函数模板
// 3.函数模板可以发生函数重载
// 4.如果函数模板可以产生更好的匹配,优先调用函数模板

//普通函数
void myAdd01(int a, int b)
{
    cout << "调用普通函数" << endl;
}
template <typename T>
void myAdd01(T a, T b)
{
    cout << "调用的函数模板" << endl;
}

template <typename T>
void myAdd01(T a, T b, T c)
{
    cout << "调用的重载的函数模板" << endl;
}

void test01()
{
    int a = 10;
    int b = 20;
    int c = 30;
    // 1. 优先调用普通函数
    myAdd01(a, b);

    // 2.通过空模板的参数列表,强制调用函数模板
    myAdd01<>(a, b);
    myAdd01<>(a, b);

    // 3.函数模板可以发生函数重载
    myAdd01<>(a, b, c);

    // 4.如果函数模板可以产生更好的匹配,优先调用函数模板f
    char c1 = 'a';
    char c2 = 'b';
    myAdd01(c1, c2);
}
int main()
{
    test01();

    return 0;
}

总结:既然提供了函数模板,最好就不要提供普通函数,否则容易出现二义性。

1.2.6 模板的局限性

局限性:

        模板的通用性并不是万能的

#include <iostream>
using namespace std;
//模板的局限性
//模板并不是万能的,有些特点的数据类型,需要用具体的方式做特殊实现
class Person {
public:
    Person(string name, int age)
    {
        this->m_Name = name;
        this->m_Age = age;
    }
    string m_Name;
    int m_Age;
};
//对比两个数据是否相等函数
template <class T>
bool myCompare(T a, T b)
{
    if (a == b) {
        return true;
    } else {
        return false;
    }
}
//重载operator ==
bool operator==(Person a, Person b)
{
    if (a.m_Name == b.m_Name && a.m_Age == b.m_Age) {
        return true;
    } else {
        return false;
    }
}
//利用具体化的Person的版本实现代码,具体化优先调用
// template <>
// bool myCompare(Person a, Person b)
//{
//    if (a.m_Name == b.m_Name && b.m_Age == b.m_Age) {
//        return true;
//    } else {
//        return false;
//    }
//}

void test01()
{
    int a = 10;
    int b = 20;
    bool c = myCompare(a, b);
    if (c) {
        cout << "相等" << endl;

    } else {
        cout << "不相等" << endl;
    }
}

void test02()
{
    Person a("Tom", 12);
    Person b("Tom", 12);

    bool c = myCompare(a, b); //报错,不能对比两个Person类,1.需要重载==运算符 2.在写一个重载
    if (c) {
        cout << "相等" << endl;

    } else {
        cout << "不相等" << endl;
    }
   
}
int main()
{
    test02();

    return 0;
}

总结:
        利用具体化的模板,可以解决自定义类型的通用化
        学习模板并不是为了写模板,而是在STL能够运用系统提供的模板

1.3 类模板

1.3.1 类模板语法

类模板作用:
        建立一个通用的类,类中的成员 数据类型可以不具体指定,用一个虚拟的类型来表示。

语法:
        template<typename T>
        类

解释:
        template --声明创建模板
        typename -- 表明后面的符号是一种数据类型,可以用class代替
        T -- 通用的数据类型,名称可以替换,通常为大写字母

#include <iostream>
using namespace std;
//类模板
template <class NameType, class AgeType>
class Person {
public:
    Person(NameType name, AgeType age)
    {
        m_Name = name;
        m_Age = age;
    }
    void showPerson()
    {

        cout << this->m_Name << " " << this->m_Age << endl;
    }
    NameType m_Name;
    AgeType m_Age;
};
void test01()
{
    Person<string, int> p("张三", 2);
    p.showPerson();
}

void test02()
{
}
int main()
{
    test01();

    return 0;
}

1.3.2 类模板和函数模板的区别

类模板与函数模板区别主要有两点:
        1.类模板没有自动类型推导的使用方式
        2.类模板在模板参数列表中可以有默认参数

#include <iostream>
using namespace std;
//类模板和函数模板的区别
template <class NameType, class AgeType = int> // age默认类型整型
class Person {
public:
    NameType m_Name;
    AgeType m_Age;

    Person(NameType name, AgeType age)

    {
        this->m_Name = name;
        this->m_Age = age;
    }
    void showPerson()
    {

        cout << this->m_Name << " " << this->m_Age << endl;
    }
};
void test01()
{
    // 1.类模板没有自动类型推导
    Person p("张三", 21); // C++17之后支持自动类型推导
    Person<string, int> p1("张三", 2);
    p.showPerson();
}

void test02()
{
    // 2.类模板在模板参数中可以有默认参数
    Person<string> p("李四", 12);
    Person p1("李四", 13);
    p.showPerson();
    p1.showPerson();
}
int main()
{
    test02();

    return 0;
}

总结:
        类模板只能用显示指定类型
        类模板可以有默认参数

1.3.3类模板成员函数创建时机

#include <iostream>
using namespace std;
//类模板中成员函数调用时机

//类模板成员函数和普通类中的成员函数创建时机是区别的
// 1.普通类中的成员函数一开始就可以创建
// 2类模板中的成员函数调用时创建.
class Person1 {
public:
    void show_Person1()
    {
        cout << "show_Person1" << endl;
    }
};
class Person2 {
public:
    void show_Person2()
    {
        cout << "show_Person2" << endl;
    }
};

template <class T>
class myClass {
public:
    T obj;
    //类模板中的成员函数
    void func1()
    {
        obj.show_Person1();
    }
    void func2()
    {
        obj.show_Person2();
    }
};
void test01()
{
    //类模板在调用时,才会运行
    myClass<Person1> m; //类模板需要写<>
    m.func1(); //可以调用
//    m.func2();//不可以调用
}

void test02()
{
    myClass<Person2> m; //类模板需要写<>
//    m.func1(); //不可以调用
    m.func2();//可以调用
}
int main()
{
    test02();

    return 0;
}

1.3.4 类模板对象做函数参数

学习目标:

        类模板实例化出的对象,向函数传参的方式

一共有三种方式:

1.指定传入类型 --直接显示对象的数据类型

2.参数模板化 --将对象中的参数变为模板进行传递

3.整个类模板化 --将这个对象类型 模板化进行传递

#include <iostream>
using namespace std;
//类模板对象做函数参数
template <class T1, class T2>
class Person {
public:
    T1 m_Name;
    T2 m_Age;
    Person(T1 name, T2 age)
    {
        this->m_Name = name;
        this->m_Age = age;
    }
    void showPerson()
    {
        cout << "姓名:" << this->m_Name << endl;
        cout << "年龄:" << this->m_Age << endl;
        cout << "T1的类型为" << typeid(T1).name() << endl;
        cout << "T2的类型为" << typeid(T2).name() << endl;
    }
};
// 1.指定传入类型
void printPerson1(Person<string, int>& p)
{
    p.showPerson();
}
// 2.参数模板化
template <class T1, class T2>
void printPerson2(Person<T1, T2>& p)
{
    p.showPerson();
}
// 3.整个类模板化
template <class T>
void printPerson3(T& p)
{
    p.showPerson();
}
void test01()
{
    Person<string, int> p("张三", 18);
    printPerson1(p);
}

void test02()
{
    Person<string, int> p("李四", 18);
    printPerson2(p);
}
void test03()
{
    Person<string, int> p("王五", 18);
    printPerson3(p);
}
int main()
{
    test03();
    //最常用第一种
    return 0;
}

总结:第一种最常用

1.3.5 类模板与继承

#include <iostream>
using namespace std;
//类模板与继承
template <class T>
class Base {
    T m;
};

// class Son:public Base //错误,必须知道父类种T数据类型才可以继承给子类
class Son : public Base<int> //知道类型就可以继承了
{
};

template <class T1, class T2>
class Son2 : public Base<T2> //知道类型就可以继承了
{
public:
    Son2()
    {
        cout << "t1的类型为:" << typeid(T1).name() << endl;
        cout << "t2的类型为:" << typeid(T2).name() << endl;
    }
    T1 obj;
};
void test01()
{
    Son s1; //可以实例化
}

//如果想灵活的指定父类中的数据类型,需要变为子类模板
void test02()
{
    Son2<int, char> s2;
}

int main()
{
    test02();
    return 0;
}

 总结:如果父类是类模板,子类在继承时要指定父类的数据类型,或者类变为类模板。

1.3.6 类模板成员函数类外实现

学习目标:能够掌握类模板中的成员函数类外实现

#include <iostream>
using namespace std;
//类模板中成员函数类外实现
template <class T1, class T2>
class Person {
public:
    T1 m_Name;
    T2 m_Age;
    //    Person(T1 name, T2 age)
    //    {
    //        this->m_Name = name;
    //        this->m_Age = age;
    //    }

    //    void showPerson()
    //    {
    //        cout << "姓名" << this->m_Name << endl;
    //        cout << "年龄" << this->m_Age << endl;
    //    }

    //如果在类外实现的化需要,类内声明,类外实现
    Person(T1 name, T2 age);
    void showPerson();
};

//构造函数类外实现
template <class T1, class T2>
Person<T1, T2>::Person(T1 name, T2 age)
{

    this->m_Name = name;
    this->m_Age = age;
}

template <class T1, class T2>
void Person<T1, T2>::showPerson()
{
    cout << "姓名" << this->m_Name << endl;
    cout << "年龄" << this->m_Age << endl;
}
void test01()
{
    Person p("张三", 14);
    p.showPerson();
}

void test02()
{
}

int main()
{
    test01();
    return 0;
}

1.3.7 类模板分文件编写

//#include "person.h" //包含h会报错,因为包含头文件,头文件是模板运行才知道数据类型
//#include "person.cpp" //包含cpp不会报错
//第二种方法,将.h和.cpp文件写入一起,后缀名为.hpp文件,代表类模板
#include "person.hpp"

#include <iostream>
#include <string>
using namespace std;

//类模板分文件编写问题及解决
// template <class T1, class T2>
// class Person {
// public:
//    T1 m_Name;
//    T2 m_Age;
//    Person(T1 name, T2 age);
//    void showPerson();
//};

// template <class T1, class T2>
// Person<T1, T2>::Person(T1 name, T2 age)
//{
//     this->m_Name = name;
//     this->m_Age = age;
// }
// template <class T1, class T2>
// void Person<T1, T2>::showPerson()
//{
//     cout << "年龄" << this->m_Age << endl;
// }

void test01()
{
    //现在运行报错
    Person<string, int> p("李四", 12);
    p.showPerson();
}
int main()
{
    test01();
    return 0;
}

person.hpp

#ifndef PERSON_H
#define PERSON_H
#pragma once
#include <iostream>

using namespace std;

template <class T1, class T2>
class Person {
public:
    T1 m_Name;
    T2 m_Age;
    Person(T1 name, T2 age);
    void showPerson();
};

template <class T1, class T2>
Person<T1, T2>::Person(T1 name, T2 age)
{
    this->m_Name = name;
    this->m_Age = age;
}
template <class T1, class T2>
void Person<T1, T2>::showPerson()
{
    cout << "年龄" << this->m_Age << endl;
}

#endif // PERSON_H

 总结:主流的解决方式是第二种,写到一起,改名称为hpp

1.3.8 类模板与友元


#include <iostream>
#include <string>
using namespace std;

//先让编译器知道有模板和类的存在
template <class T1, class T2>
class Person;

// 2.全局函数,类外实现
//函数模板
template <class T1, class T2>
void printPerson2(Person<T1, T2> p)
{
    cout << "姓名" << p.m_Name << endl;
    cout << "年龄" << p.m_Age << endl;
}

//通过全局函数,打印Person信息
template <class T1, class T2>
class Person {

private:
    T1 m_Name;
    T2 m_Age;

private:
    // 1.全局函数,类内实现

    //在私有属性里加入friend 声明为类的友元
    //全局函数?不太懂
    //全局函数,似乎只要是友元,那么他就是全局函数
    friend void printPerson(Person<T1, T2> p)
    {
        cout << "姓名" << p.m_Name << endl;
        cout << "年龄" << p.m_Age << endl;
    }

    // 2.全局函数,类外实现
    //普通函数
    //加空模板参数列表
    //如果全局函数是类外实现,让编译器提前知道这个函数的存在
    friend void printPerson2<>(Person<T1, T2> p);

public:
    Person(T1 name, T2 age)
    {
        this->m_Name = name;
        this->m_Age = age;
    }
};

void test01()
{
    Person<string, int> p("李四", 12);
    printPerson(p);
}
void test02()
{
    Person<string, int> p("张三", 1);
    printPerson2(p);
}
int main()
{
    test02();
    return 0;
}

 

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

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

制造业项目管理软件如何帮助企业做好项目费用管理?

在项目导向型制造型企业中&#xff0c;项目的成本管理与费用控制是企业进行项目评价与利润管控、指导市场选择和项目筛选的重要手段。而传统的手工管理模式下&#xff0c;制造企业管理层很难快速了解到哪些项目出现了延误、哪些项目发生了费用超支、哪些项目产生了变更等问题与…

C#,图像二值化(14)——全局阈值的最佳迭代算法及源代码

1、图像二值化 图像二值化是将彩色图像转换为黑白图像。大多数计算机视觉应用程序将图片转换为二进制表示。图像越是未经处理&#xff0c;计算机就越容易解释其基本特征。 二值化过程 在计算机存储器中&#xff0c;所有文件通常以灰度级的形式存储&#xff0c;灰度级具有从0…

欢迎来到,个人数据安全“世界杯”

2022年国际足联世界杯&#xff0c;巴西止步8强&#xff0c;克罗地亚挺到半决赛&#xff0c;阿根廷与法国双强对决最终阿根廷点球大战胜出……精彩纷呈的世界杯已经落幕&#xff0c;而我们因足球而起的激情和热爱不会消退。世界杯是属于每个人的&#xff0c;每个球迷在世界杯中都…

03-redis篇 架构设计之一: 主从复制

目录 第一篇: 主从复制 二. 实践操作 1. 准备工作 -> ps: 安装redis的文章: docker版 的redis安装 2. 制作docker镜像 -> 2.1 制作redis6379 -> 2.2 制作redis6380 -> 2.3 制作redis6381 3. 查看主镜像redis6379的ip地址 -> 3.1 IPAddress位置在这: …

【数据库数据恢复】mdb_catalog.wt文件丢失的MongoDB数据恢复案例

MongoDB数据库数据恢复环境&#xff1a; MongoDB数据库部署在一台虚拟机上&#xff0c;虚拟机操作系统为Windows Server2012。 MongoDB数据库故障&分析&#xff1a; 由于业务发展需求&#xff0c;需要对MongoDB数据库内的文件进行迁移&#xff0c;在MongoDB服务开启的状态…

内部排序:希尔排序

希尔排序&#xff0c;又称为“缩小增量排序”&#xff0c;是直接插入排序的优化。 对于直接插入排序&#xff0c;当待排记录序列处于正序时&#xff0c;时间复杂度可达O(n)&#xff0c;若待排记录序列越接近有序&#xff0c;直接插入排序越高效。希尔排序的思想正是基于这个点…

QT(5)-QHeaderView

QHeaderView1 说明2 函数2.1 级联调整大小2.2 默认对齐方式2.3 count()2.4 表头默认单元格大小2.5 hiddenSectionCount()2.6 分区显示和隐藏2.7 表头高亮2.8 是否可以移动第一列2.7 是否显示排序索引2.8 表头长度2.9 逻辑索引2.10 表头分区最大/小大小2.11 移动分区2.12 表头偏…

Qlik帮助提升数据素养:新一代打工人“必备招式”

“营销”在业务推进过程中扮演着至关重要的角色。然而&#xff0c;当前营销的影响力却往往未得到广泛理解和重视。 在数字世界里&#xff0c;数据浩瀚如海&#xff0c;但如果“探险者”没有乘风破浪的能力&#xff0c;这片数据汪洋只能沉寂在角落里“吃灰”。而数据素养&#…

Ubuntu20.04 rosdep 失败解决方法

参考文章http://www.autolabor.com.cn/book/ROSTutorials/chapter1/12-roskai-fa-gong-ju-an-zhuang/124-an-zhuang-ros.htmlsudo gedit ./rosdistro/__init__.py sudo gedit ./rosdep2/gbpdistro_support.py sudo gedit ./rosdep2/sources_list.py sudo gedit ./rosdep2/rep3.…

厚积薄发打卡Day112:堆栈实践(二)<汉诺塔问题>

厚积薄发打卡Day112&#xff1a;堆栈实践&#xff08;二&#xff09;&#xff1c;汉诺塔问题&#xff1e; 问题 相传在古印度圣庙中&#xff0c;有一种被称为汉诺塔(Hanoi)的游戏。该游戏是在一块铜板装置上&#xff0c;有三根杆(编号A、B、C)&#xff0c;在A杆自下而上、由大…

Jvm知识点二(GC)

GC 相关知识点一、垃圾收集器二、 java 中的引用三、 怎么判断对象是否可以被回收&#xff1f;四、 Java对象在虚拟机中的生命周期五、垃圾收集算法标记-清除算法复制算法补充知识点深拷贝和浅拷贝标记-压缩算法&#xff08;Mark-Compact&#xff09;分代收集算法Java堆的分区六…

SSH实验部署

一&#xff0c;实验要求 1&#xff0c;两台机器&#xff1a;第一台机器作为客户端&#xff0c;第二台机器作为服务器&#xff0c;在第一台使用rhce用户免 密登录第二台机器 2&#xff0c;禁止root用户远程登录和设置三个用户sshuser1, sshuser2, sshuser3&#xff0c; 只允许ss…

三维数学(二)

欧拉角 使用物体在三个旋转轴上的旋转角度来保存方位 API&#xff1a; Transform.eulerAngles&#xff1a;返回或设置物体的欧拉角 优点&#xff1a; 1.仅使用三个数字表达方位&#xff0c;占用空间小 2.沿坐标轴旋转的单位为角度&#xff0c;符合人的思考方式 3.任意…

OSPF网络类型实验配置(华为)

OSPF网络类型实验配置&#xff08;华为&#xff09;&#xff1a; 根据实验要求&#xff0c;我们可以把其拆分成为两个部分来做&#xff0c;分别做两个部分的MGRE: 通过拆分可以更加直观的看到路由器之间的信息传输&#xff0c;然后分别做R1,R2,R3和R1,R4,R5的MGRE&#xff1a;…

【Xilinx】如何自动格式化Verilog代码

开发环境VivadoVSCode 【Xilinx】自动格式化Verilog代码前言一、安装VSCode并修改Vivado的默认编辑器二、安装Verilog插件1. 语法插件2. 格式化插件三、演示&#xff1a;如何代码格式化1. 插件演示2. 修改默认插件附录前言 有时候接手别人的代码&#xff0c;或者从网上找的开源…

2023学习心得01

2023年&#xff0c;加足马力&#xff0c;继续提升自己&#xff01; 这次来分享下最近的学习心得&#xff0c;以便自己后续回顾可快速上手 按键框架数字&#xff0c;文字取模菜单框架Main总体框架1.首先来分析按键的框架&#xff0c;这里用到了函数指针&#xff0c;不同的可以…

并查集(C++)

根据下面这道题讲下并查集 &#xff08;其实本来是写题解的…写着写着就变成算法说明了&#xff09; [蓝桥杯 2017 国 C] 合根植物&#xff08;C&#xff0c;并查集&#xff09; 题目描述 w 星球的一个种植园&#xff0c;被分成 mnm \times nmn 个小格子&#xff08;东西方…

【深度腐蚀】深入聊聊KMP算法

思路分析&#xff1a;主串str遍历主串j子串sub遍历子串iKMP算法是一种字符串匹配算法&#xff0c;他通过Next 数组能使i不回退&#xff0c;这样大大减少了无效的比对&#xff0c;提高了字符串匹配的速度。Next数组&#xff1a;要想让i不回退&#xff0c;就需要让j回退到合适的位…

HTTPS】HTTPS过程详解,tcpdump抓包 全过程分析

RFC中的HTTPS交互过程如下&#xff1a; 抓包分析 Client Hello 客户端支持的TLS最高版本号 客户端生成的随机数 客户端支持的加密套件 主机名server_name cipher suite怎么理解 名字为 ECDH-ECDSA-AES128-SHA256 的CipherSuite 使用 ECDH做密钥交换&#xff0c; 使用ECDS…

21. 反爬工程师都会用的手段,IP限制反爬 - 爬虫训练场

本篇博客我们实现的案例是 IP 限制反爬&#xff0c;翻译过来就是每个 IP 在规定时间内限制访问次数。 例如&#xff0c;可以限制单 IP 每秒访问 5 次&#xff0c;超过之后就会返回 403 错误。 Flask 实现 IP 限制使用 Flask 插件自定义中间件限制 IP自定义请求钩子使用 Flask 插…