C++泛型编程综合讲解

目录
  • 函数模板
  • 类模板
  • 函数符(Function)
  • 包装器

函数模板

进一步把函数中的或类中的数据类型进一步抽象,这个抽象的类型就叫泛型

模板:函数模板,类模板

模板就是把函数(或类)中的类型抽象出来,有指定类型方可使用

模板可以有默认类型,类模板规则(函数模板,不存在规则):从右到左

模板编译机制:

  • 编译器并不是把函数(类)模板处理成能够处理任何类型的函数(类),而是一个函数(类)的生成器。
  • 函数(类)模板通过具体类型产生不同的函数实例(类实体)。
  • 编译器会对函数(类)模板进行两次编译,第一次在声明的地方对模板本身进行编译(主要是对语法进行检查,在调用的地方对参数替换),再次对代码进行编译,二次编译也被称之为延时编译。

注意:模板函数需要编译两次,是慢于定义函数的原因

关键字template是模板标识符

<>是泛型,指定的参数类型列表

class用来修饰泛型,typename也可进行修饰

#include <iostream>
using namespace std;
//int add(int a,int b){
//    return a+b;
//}
//float add(float a,float b){
//    return a+b;
//}
//string add(string a,string b){
//    return a+b;
//}
//抽象的泛型
template<class T>
T add(T a,T b){
    cout << "i am is template" << endl;
    return a+b;
}
int main()
{
    int a=10,b=20;
    cout << add(a,b) << endl;
    float a1=5.21;
    float b1=13.14;
    cout << add(a1,b1) << endl;
    string a2="yao",b2="liang";
    cout << add(a2,b2) << endl;//隐式调用
    cout << add<string>(a2,b2) << endl;//显示调用
    return 0;
}

显示调用和隐式调用

#include <iostream>
using namespace std;
//抽象的泛型
typename<class T>
T add(T a,T b){
    return a+b;
}
int main()
{
    string a2="yao",b2="liang";
    cout << add(a2,b2) << endl;//隐式调用
    cout << add<string>(a2,b2) << endl;//显示调用
    return 0;
}

函数模板的特化

前提:模板的特化(泛型没有制定类型)是依赖基础模板的

产生原因:当函数的算法逻辑与实际的参数类型不匹配时,就应该对类型进行特化

#include <iostream>
using namespace std;
template  <class T>
T compair(T t1,T t2){
    return t1>t2?t1:t2;
}
//对基础模板进行全特化(函数模板只能全特化,不能偏特化)
template <>
const char* compair(const char* str1,const char* str2){
    return string(str1)>string(str2)?str1:str2;
}
int main()
{
    int a=10,b=20;
    cout << compair(a,b) << endl;
    const char* str1="yaoliang";
    const char* str2="yao";
    cout << compair(str1,str2) << endl;
    return 0;
}

类型可以传*号

#include <iostream>
using namespace std;
template  <class T>
T compair(T t1,T t2){//char *t1=name;
    cout << string(t1) << endl;
}
int main()
{
    char *name="minmin";
    char *name1="sun";
    compair(name,name1);
//    int a=10;
//    int *p=&a;
//    int *q=&a;
//    compair(p,q);
    return 0;
}

函数模板的调用优先级

函数实例>匹配的特化模板>基础模板

#include <iostream>
using namespace std;
template  <class T>
T compair(T t1,T t2){
    cout << "i am is basics" <<endl;
    return t1>t2?t1:t2;
}
//对基础模板进行全特化(函数模板只能全特化,不能偏特化)
template <>
const char* compair(const char* str1,const char* str2){
    cout << "i am is specialization" <<endl;
    return string(str1)>string(str2)?str1:str2;
}
inline int compair(int a,int b){
    cout << "i am is inline fun" << endl;
    return a>b?a:b;
}
int main()
{
    int a=10,b=20;
    cout << compair(a,b) << endl;
    const char* str1="yaoliang";
    const char* str2="yao";
    //如果是隐式调用,优先调用与之类型相匹配的特化模板
    cout << compair(str1,str2) << endl;
    //显性调用,直接调用
    cout << compair<const char*>(str1,str2) << endl;
    cout << compair<int>(a,b) << endl;
    return 0;
}

函数模板的实参推演

函数模板具有函数特性:函数重载

#include <iostream>
using namespace std;
template  <class T>
T add(T t1,T t2){
    cout << "i am is one_basics" <<endl;
    return t1+t2;
}
template  <class T1,class T2>
T1 add(T1 t1,T2 t2){
    cout << "i am is two_basics" <<endl;
    return t1+t2;
}
int main()
{
    int a=10,b=20;
    cout << add(a,b) << endl;
    double c=13.14;
    cout << add(c,a) << endl;
    return 0;
}

函数泛型不仅是一个单一抽象类型,也可以是一个组合类型。

#include <iostream>
#include <typeinfo>//信息识别头
using namespace std;
template <class T>
void my_funtion(T t){
    cout << "i am is basics" << endl;
    cout << typeid (t).name() << endl;
}
template <>
void my_funtion(int* t){
    cout << "指针类型的特化" << endl;
    cout << typeid (t).name() << endl;
}
template <class Ret,class Arg1,class Arg2>
void my_funtion(Ret (*arg)(Arg1,Arg2)){
    cout << typeid (Ret).name() << endl;
    cout << typeid (Arg1).name() << endl;
    cout << typeid (Arg2).name() << endl;
    cout << "指针类型的复合模板" << endl;
}
int add(int a,int b){
    return  a+b;
}
int main()
{
    int a=10;
    my_funtion(a);
    int *p=&a;
    my_funtion(p);
    my_funtion(add);
    return 0;
}

在c++11关于函数模板的可变参符号…

…如果修饰类型(变量),则表示类型(变量)不定引数,个数不同,类型不同的多个参数。

#include <iostream>
using namespace std;
//函数实例
void print(){
};
template <class FirstArg,class... Args>
void print(FirstArg firstArg,Args... args){//int firstArg=100,...(3.14,"yaoliang")
    									//3.14 ...("yaoliang")
    									//"yaoliang" ...()
    cout << firstArg << " ";
    print(args...);
}
int main()
{
   print(100,3.14,"yaoliang");
   return 0;
}

类模板

像声明一个类一样声明一个模板,无隐式调用,模板规则:使用默认泛型参数类型,从右向左依次指定

#include <iostream>
using namespace std;
template <class T1,class T2>
class Person{
private:
    T1 _name;
    T2 _age;
public:
    Person(T1 name,T2 age){
        this->_age=age;
        this->_name=name;
    }
    int getAge(){
        return this->_age;
    }
    string getName(){
        return  this->_name;
    }
    virtual void showInfo(){
        cout << "姓名:" << this->_name << ",年龄:" << this->_age << endl;
    }
};
template <class T1,class T2,class T3=int>
class Stu:public Person<T1,T2>
{
private:
    const T3 _id;
    static int count;
public:
    Stu(T1 name,T2 age,T3 id):Person<T1,T2>(name,age),_id(id){
        count++;
    }
    void showInfo()override{
        cout << "学号:" << this->_id << ",姓名:" << this->getName() << ",年龄:" << this->getAge() << endl;
    }
    static int get_count(){
        return count;
    }
};
template <class T1,class T2,class T3>
int Stu<T1,T2,T3>::count=0;
int main()
{
    Person<string,int> *person=new Stu<string,int,int>("yao",19,1);
    person->showInfo();
    delete person;
    Stu<string,int> stu("sunsun",18,2);//使用缺省类型,从右往左
    stu.showInfo();
    cout << Stu<string,int,int>::get_count() << endl;
    return 0;
}

分文件编程实现一个顺序栈

注意: .hpp是类模板文件,声明和定义在同一个文件中

stack_cpp.hpp:

#ifndef MY_STACK_HPP
#define MY_STACK_HPP
#include <exception>
#include <stdexcept>
#include <iostream>
using namespace std;
template <class T>
class my_stack{
private:
    T* m_data;
    int capacity;
    int size;
public:
    my_stack(int c=10);
    ~my_stack();
    bool full();
    bool empty();
    void push(const T& val);
    void pop();
    T& top();
};
#endif // MY_STACK_HPP
template<class T>
my_stack<T>::my_stack(int c)
{
    this->capacity=c;
    this->m_data=new T[capacity];
    this->size=0;
}
template<class T>
my_stack<T>::~my_stack()
{
    if(nullptr!=this->m_data){
        delete [] this->m_data;
        this->m_data=nullptr;
    }
    capacity=size=0;
}
template<class T>
bool my_stack<T>::full()
{
    return size==capacity;
}
template<class T>
bool my_stack<T>::empty()
{
    return size==0;
}
template<class T>
void my_stack<T>::push(const T &val)
{
    if(full()){
        return;
    }
    m_data[size]=val;
    size++;
}
template<class T>
void my_stack<T>::pop()
{
    if(this->empty()){
        throw range_error("空了");
    }
    size--;
}
template<class T>
T &my_stack<T>::top()
{
    return m_data[size-1];
}

main.cpp:

#include <iostream>
#include "my_stack.hpp"
using namespace std;
int main()
{
    my_stack<int> s;
    s.push(1);
    s.push(2);
    s.push(3);
    while (!s.empty()) {
        cout << s.top() << endl;
        s.pop();
    }
    return 0;
}

内嵌类

为外围类服务,不影响外围类

#include <iostream>
#include <vector>
using namespace std;
template <class T>
class A{
public:
    int a;
    class B{
    public:
        int b=10;
    };
};
int main()
{
    cout << sizeof (A<int>) << endl;//4
    A<int>::B b_obj;
    cout << b_obj.b << endl;
    cout << "------------vetor容器---------------" << endl;
    vector<int> v;
    for(int i=0;i<10;i++){
        v.push_back(rand()%100+1);
    }
    vector<int>::iterator it;
    for(it=v.begin();it!=v.end();it++){
        cout << *it << "   ";
    }
    cout << endl;
    return 0;
}

注意: 外围类和内围类之间不能相互访问,特殊的:静态属性

类模板的特化

#include <iostream>
using namespace std;
template <class T>
class A{
public:
    A(){
        cout << " A basics" << endl;
    }
};
template <>
class A<int>
{
public:
    A(){
        cout << " A 全特化 " << endl;
    }
};
template <class T>
class A<T*>
{
public:
    A(){
        cout << " A 偏特化" << endl;
    }
};
template <>
class A<int*>
{
public:
    A(){
        cout << " A 的全特化" << endl;
    }
};
template <class Ret,class Arg1,class Arg2>
class A<Ret (*)(Arg1,Arg2)>{
public:
    A(){
        cout << " A 的偏特化" << endl;
    }
};
int add(int a,int b){
    return  a+b;
}
int main()
{
    A<int> a;
    A<float> a1;
    A<int *> a2;
    A<int(*)(int,int)> a3=add;
    return 0;
}

类实例>匹配的全特化模板>匹配的偏特化模板>基础模板

函数符(Function)

函数对象(Functor),仿函数

保存函数调用签名的形式:

  • 全局函数指针
  • 成员指针
  • 函数对象
  • lambda表达式

函数对象:是类对象,这个类对象的类中有一个小括号重载运算符函数。

#include <iostream>
using namespace std;
template<class T>
class A{
private:
    T str;
public:
    inline A(const T& t){
        this->str=t;
    }
    inline void  operator()(){
        cout << this->str << endl;
    }
};
void showInfo(){
    cout << "hello" << endl;
}
int main()
{
    showInfo();
    cout << "---------------------------------" << endl;
    A<string> a("functor is hello");
    a();
    return 0;
}

特点:

函数对象是类对象,当类对象调用成员函数时,函数符合内联条件,自动升级为内联函数,调用比普通函数效率高

函数对象可以直接使用类中定义的属性

函数对象具有具体的类型

函数对象一般不会单独使用,一般作为算法策略使用:

#include <iostream>
using namespace std;
template <class T>
T my_greate(T t1,T t2){
    return t1>t2?t1:t2;
}
template <class T>
T my_less(T t1,T t2){
    return t1<t2?t1:t2;
}
template <class T,class Compair>//Compair是获取到的函数类型 T是获取到的数据类型
T compair(T t1,T t2,Compair f){//Compair f=my_greate<int>
    return f(t1,t2);
}
//声明两个函数对象
template <class T>
class my_Greate{
public:
    T operator()(T t1,T t2){
        return t1>t2?t1:t2;
    }
};
template <class T>
class my_Less{
public:
    T operator()(T t1,T t2){
        return t1<t2?t1:t2;
    }
};
int main()
{
    int a=10,b=20;
    cout << "获取较大的值" << compair(a,b,my_greate<int>) << endl;
    cout << "获取较小的值" << compair(a,b,my_less<int>) << endl;
    cout << "使用函数对象,提高调用效率" << endl;
    cout << "获取较大的值" << compair(a,b,my_Greate<int>()) << endl;
    cout << "获取较小的值" << compair(a,b,my_Less<int>()) << endl;
    return 0;
}

函数对象术语

当函数对象的类中的小阔号运算符只有一个形参,所定义对象时,这个对象叫做一元函数对象

当函数对象的类中的小阔号运算符只有二个形参,所定义对象时,这个对象叫做二元函数对象

当函数对象的类中的小阔号运算符有多个形参,所定义对象时,这个对象叫做多元函数对象

当函数对象的类中的小阔号运算符返回值时一个bool类型,这个对象叫做谓词(Predicate)

匿名函数对象Lambda表达式

Lambda表达式分析:

  • []是函数对象的构造函数中的形参,获取外部实参时传递的形式
  • []为空时,代表无参的空构造,对于lambda不进行捕获
  • [=]相当于函数对象中的类中的构造函数为拷贝传参(值的传递)
  • [&]相当于函数对象中的类中的构造函数为引用传递(别名)
  • ()相当于小阔号运算符的形参列表
  • {}相当于括号运算符的函数体
  • 在lambda的形参列表后使用->返回值类型,明确返回值的类型
#include <iostream>
using namespace std;
class Lambda{
private:
//    int _a;
    int& _b;
public:
//    Lambda(){
//    }
//    Lambda(int& a){
//        //相当于构造函数中是一个值的拷贝
//        this->_a=a;
//    }
    Lambda(int& b):_b(b){
        //相当于构造函数中是一个值的拷贝
        this->_b=b;
    }
    void operator()(){
        cout << "hello world!" << endl;
    }
};
int main()
{
    //c++11 auto关键字:表示由编译器自动推导出的数据类型。不可作为函数形参
    auto f=[](){cout << "hello world" << endl;};
    f();
//    Lambda()();
//    auto f1=Lambda();
//    f1();
//    int a=100;
//    auto f2=[=](){
//        cout << a << endl;
//    };
//    f2();
    int b=10;
    cout << "b的地址:" << &b << endl;
    auto f3=[&](){
        cout << "b的地址:" << &b << endl;
    };
    f3();
    int x=100,y=200;
    auto f4=[&]()mutable{//mutable易变关键字,与const关键字相反
        int temp=x;
        x=y;
        y=temp;
    };
    f4();
    cout << "x=" << x << "  y=" << y << endl;
    return 0;
}

包装器

类模板std::function 是通用的多态函数封装器。 std::function 的实例能存储、复制及调用任何可调用对象。C++语言中有多种可调用对象:函数、函数指针、lambda表达式、bind创建的对象以及重载了函数调用运算符的类(仿函数)等。

和其他对象一样,可调用对象也有类型。如:每个lambda有它自己唯一的(未命名)类类型;函数及函数指针的类型则由其返回值类型和实参类型决定。然而,不同类型的可调用对象可能共享同一种调用形式。调用形式指明了返回的类型以及传递给调用的实参类型。一种调用形式对应一个函数(function)类型。

标准使用:

#include <iostream>
#include <functional>
using namespace std;
int add(int a,int b){//add函数类型:int (int ,int )
    return a+b;
}
class A{
public:
    int add(int a,int b){//int A::(A* const,int,int)
        return a+b;
    }
};
class B{
public:
    int operator()(int a,int b){//int A::(int,int)
        return a+b;
    }
};
int main()
{
    //使用标准包装器function包装全局函数
    function<int (int,int)> f1=add;
    cout << f1(10,20) << endl;
    //使用标准包装器function包装类成员函数
    A a;
    function<int(A* const,int,int)> f2=&A::add;
    cout << f2(&a,20,30) << endl;
    //使用标准包装器function包装一个函数对象
    function<int(int,int)> f3=B();
    cout << f3(10,20) <<endl;
    //使用标准包装器function包装一个Lambda表达式
    function <int (int,int)> f4=[](int a,int b){return a+b;};
    cout << f4(100,220) << endl;
    return 0;
}

封装一个包装器:

#include <iostream>
#include <functional>
using namespace std;
template <class T>
class My_function{
public:
    My_function(){
        cout << "my_function is basics" << endl;
    }
};
//模板偏特化
template<class Ret,class Arg1,class Arg2>
class My_function<Ret (Arg1,Arg2)>
{
private:
    //typedef Ret(*Pfunc)(Arg1,Arg2);
    using Pfunc=Ret (*)(Arg1,Arg2);
    Pfunc f;
public:
    My_function(Pfunc f){
        this->f=f;
    }
    //包装器核心
    Ret operator()(Arg1 arg1,Arg2 arg2){
        return f(arg1,arg2);
    }
};
int add(int a,int b){//类型 int (int ,int)
    return a+b;
}
int main()
{
    My_function<int (int,int)> f1=add;
    cout << f1(10,20) << endl;
    std::function<int(int,int)> f2=add;
    cout << f2(20,40) << endl;
    return 0;
}

到此这篇关于C++泛型编程综合讲解的文章就介绍到这了,更多相关C++泛型编程内容请搜索我们以前的文章或继续浏览下面的相关文章希望大家以后多多支持我们!

(0)

相关推荐

  • C++超详细讲解泛型

    目录 1.了解泛型编程 2.函数模板 2.1简单示例 2.2多个模板参数 2.3模板实例化 2.4模板和普通函数同时存在 2.5函数模板不支持定义和声明分离 3.类模板 3.1简单示例 3.2成员函数声明和定义分离 1.了解泛型编程 就好比活字印刷术,可以灵活调整印刷的板块和内容,比只能固定印刷某一个内容的雕版印刷术效率更高,也让印刷术由此得到了更广泛的应用. 在C++中,函数重载和模板的出现,让泛型编程得到了实际的应用.其中模板,就是类似活字印刷术一样的存在. 2.函数模板 八八了那么多没用的

  • C++泛型编程函(数模板+类模板)

    目录 一.函数模板 1.函数模板介绍 2.函数模板与重载函数的关系 3.函数模板实现机制 二.类模板 1.类模板基本语法 2.类模板内函数的整体布局[分文件使用类模板] 3.类模板的static与模板类的static 4.数组实现万能容器 前言: 由于C++是静态语言,也就是说使用一个数据的时候必须先指定类型,这样的操作在编译后变量的类型是无法轻易改变的,就导致扩展性太差.或者一个函数需要很多次重载的时候,代码显得冗杂,由此产生了C++函数模板. 一.函数模板 1.函数模板介绍 ① 函数模板的产

  • 详解C++泛型装饰器

    目录 c++ 装饰器 对输出的解释 总结 c++ 装饰器 本文简单写了个 c++ 装饰器,主要使用的是c++ lamda 表达式,结合完美转发技巧,在一定程度上提升性能 #define FieldSetter(name, type, field) \ type field; \ name() {} \ name(const type& field): field(field) { \ cout << "[左值 " << field << &q

  • C++使用泛型导致的膨胀问题

    目录 临峰不畏博主从事C++软件开发多年,由于之前的开发环境都是资源充足的服务器,不用考虑磁盘空间的问题.最近打算在智能家居主机的嵌入式平台上使用C++进行开发.FLASH存储空间有限,这是必须要考虑的因素,一定要重视. 如下定义两个list,元素类型不同: list<int> l1; list<string> l2; 如果是用C语来做应该怎么办?它会对应list<int>写一套代码,再对list<string>写一套.每套都有相同的成员函数,只是变量类型各

  • C++泛型编程Generic Programming的使用

    目录 一.容器 array vector deque list map 键值对key/value 二.迭代器iterator(泛型指针) 三.泛型算法Generic Programming insert()插入 erase()删除 find()用于无序搜索,搜素范围[first, last), 返回iterator, 找不到则返回last copy() 复制 泛型编程最初提出时的动机很简单直接:发明一种语言机制,能够帮助实现一个通用的标准容器库. 所谓通用的标准容器库,就是要能够做到,比如用一个

  • C++泛型模板约束深入讲解

    CPP参考:(新标准) 传送门 模板对于类型的约束: 约束 template_get_size 泛型T只允许接受类型:list<T>,其实为 C/C++ 泛型模板例化特性,但与泛型模板例化略微有些区别,因为是带泛型类型约束条件的特例化. template<typename T> class list { public: int count = 0; }; template<typename T> struct template_get_size; template<

  • C++ primer超详细讲解泛型算法

    目录 初识泛型算法 只读算法 写容器算法 定制操作 lambda表达式 lambda捕获和返回 再探迭代器 插入迭代器 iostream迭代器 反向迭代器 初识泛型算法 只读算法 只读取输入范围内的函数,不改变元素,find,accumula也是如此 (1)accumulate算法为求和算法,前两个参数指出求和元素范围,第三个是和的初值,例: int sum=accumulate(v.begin(),v.end(),0) (2)操作两个序列的算法 equal算法,确定两个序列是否保存相同的值,将

  • Springboot通过lucene实现全文检索详解流程

    目录 建立索引 检索文档 Lucene提供了一个简单却强大的应用程序接口(API),能够做全文索引和搜寻,在Java开发环境里Lucene是一个成熟的免费开放源代码工具. Lucene全文检索就是对文档中全部内容进行分词,然后对所有单词建立倒排索引的过程.主要操作是使用Lucene的API来实现对索引的增(创建索引).删(删除索引).改(修改索引).查(搜索数据). 假设我们的电脑的目录中含有很多文本文档,我们需要查找哪些文档含有某个关键词.为了实现这种功能,我们首先利用 Lucene 对这个目

  • springboot微服务Lucence实现Mysql全文检索功能

    目录 一.前言 1.1 常规调优手段 1.1.1 加索引 1.1.2 代码层优化 1.1.3 减少关联表查询 1.1.4 分库分表 1.1.5 引入第三方存储 二.一个棘手的问题 2.1 前置准备 2.1.1 创建一张表 2.1.2 插入一些数据 2.2 问题引发 2.2.1 关键字模糊查询 2.2.2 执行计划分析 2.2.3 需求引出 三.lucence与全文检索 3.1 Lucene概念 3.2 全文检索 3.3 Lucene 建立索引的过程 四.基于Lucence解决方案 4.1 需求分

  • 正则表达式——详细讲解平衡组

    这篇文章适合你吗? 要读懂这篇文章的精髓,你最好要有一点正则匹配原理的基础.比如".*?"匹配文本内容"asp163",稍懂正则表达式的人都知道可以匹配,但是你知道他的匹配过程吗?如果你不太清楚,那么下面的内容,对你来说可能不太适合,或许,看的太吃力且无法领悟平衡组的用法.因此,我建议你先了解正则表达式NFA引擎的匹配原理.想要整理一份易懂易描述的话,的确要费些时间,但不知道这篇内容会不会达到我预期的效果.慢慢完善吧~(注:这是我2010年写的,现在拿过来,有时间将

  • Log4j_配置方法(全面讲解)

    一.Log4j简介 Log4j有三个主要的组件:Loggers(记录器),Appenders (输出源)和Layouts(布局).这里可简单理解为日志类别,日志要输出的地方和日志以何种形式输出.综合使用这三个组件可以轻松地记录信息的类型和级别,并可以在运行时控制日志输出的样式和位置. 1.Loggers Loggers组件在此系统中被分为五个级别:DEBUG.INFO.WARN.ERROR和FATAL.这五个级别是有顺序的,DEBUG < INFO < WARN < ERROR <

  • 讲解ASP方面的知识比较全的asp学习教程

    讲解ASP方面的知识比较全!适合 刚开始学习ASP的朋友们 给我加分!!!!   第一部分  安装Active Server  Page的准备工作   .第一章 安装和使用   WINDOWS NT Server    ............  .第二章 安装和使用   IIS    ............  .第三章 安装和使用   SQL Server    ............  .第四章 Exchage   Active Server,    Index Server和   Ne

  • TensorFlow 实战之实现卷积神经网络的实例讲解

    本文根据最近学习TensorFlow书籍网络文章的情况,特将一些学习心得做了总结,详情如下.如有不当之处,请各位大拿多多指点,在此谢过. 一.相关性概念 1.卷积神经网络(ConvolutionNeural Network,CNN) 19世纪60年代科学家最早提出感受野(ReceptiveField).当时通过对猫视觉皮层细胞研究,科学家发现每一个视觉神经元只会处理一小块区域的视觉图像,即感受野.20世纪80年代,日本科学家提出神经认知机(Neocognitron)的概念,被视为卷积神经网络最初

  • Python3爬虫关于识别点触点选验证码的实例讲解

    上一节我们实现了极验验证码的识别,但是除了极验其实还有另一种常见的且应用广泛的验证码,比较有代表性的就是点触验证码. 可能你对这个名字比较陌生,但是肯定见过类似的验证码,比如 12306,这就是一种典型的点触验证码,如图所示: 我们需要直接点击图中符合要求的图,如果所有答案均正确才会验证成功,如果有一个答案错误,验证就会失败,这种验证码就可以称之为点触验证码. 另外还有一个专门提供点触验证码服务的站点,叫做 TouClick,其官方网站为:https://www.touclick.com/,本节

  • 通过Spring Security魔幻山谷讲解获取认证机制核心原理

    本文基于Springboot+Vue+Spring Security框架而写的原创学习笔记,demo代码参考<Spring Boot+Spring Cloud+Vue+Element项目实战:手把手教你开发权限管理系统>一书. 这是一个古老的传说. 在神秘的Web系统世界里,有一座名为SpringSecurity的山谷,它高耸入云,蔓延千里,鸟飞不过,兽攀不了.这座山谷只有一条逼仄的道路可通.然而,若要通过这条道路前往另一头的世界,就必须先拿到一块名为token的令牌,只有这样,道路上戍守关口

  • Elasticsearches打分机制讲解

    目录 一 例子 二 文档打分的运作机制:TF-IDF 2.1 词频:TF 2.2 逆文档频率:IDF 三 Lucene评分公式 四 其他的打分方法 五 配置打分模型 5.1 简要配置BM25打分模型 5.2 为BM25配置高级的settings 5.3 配置全局打分模型 六 boosting 6.1 索引期间的boosting 6.2 查询期间的boosting 6.3 跨越多个字段的查询 七 使用“解释”来理解文档是如何评分的 一 例子 现在,讲述一个真实的故事! 故事一定是伴随着赵忠祥老师的

  • C语言深入讲解宏的定义与使用方法

    目录 一.C语言中的宏定义 二.宏定义表达式 三.宏表达式与函数的对比 四.有趣的问题 五.强大的内置宏 六.小结 一.C语言中的宏定义 #define是预处理器处理的单元实体之一 #define 定义的宏可以出现在程序的任意位置 #define 定义之后的代码都可以使用这个宏 #define 定义的宏常量可以直接使用 #define 定义的宏常量本质为字面量 下面的宏常量定义正确吗? 编写代码来测试: #define ERROR -1 #define PATH1 "D:\test\test.c

随机推荐