【C++基础】08:模板

news2024/11/15 12:24:48

模板


OVERVIEW

  • 模板
      • 一、函数模板
        • 1.func template基本使用:
        • 2.func template案例:数组排序
        • 3.函数与函数模板的区别&调用规则:
        • 4.func template的局限性:
      • 二、类模板
        • 1.类模板基本使用:
        • 2.类模板与函数模板的区别:
        • 3.class template中成员函数创建时机:
        • 4.class template对象做函数参数:
        • 5.class template与继承:
        • 6.class template成员函数类外实现:
        • 7.class template成员函数分文件编写:
        • 8.类模板与友元:
      • 三、通用数组类★
        • 1.功能分析:
        • 2.功能实现:
        • 3.功能增加&程序修改:

本阶段主要针对C++泛型编程(利用模板实现)和STL技术做详细讲解,探讨C++更深层的使用。

模板就是通过建立通用的模具,从而提高程序复用性。模板主要可以分为函数模板与类模板。

一、函数模板

1.func template基本使用:

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

template<typename T>
//后紧跟着函数的定义或声明
函数模板说明
template声明创建模板
typename表明其后面的符号是一种数据类型,可以用class替代
T通用的数据类型,名称可以替换(通常为大写的字母)
#include<iostream>
using namespace std;

//1.声明一个函数模板告诉编译器T是一个通用的数据类型
template<typename T>
void mySwap(T &a, T &b){
    T temp = a;
    a = b;
    b = temp;
}

void test01(){
    int a = 10;
    int b = 20;
    //2.两种方式使用函数模板
    //(1)自动类型推导
    mySwap(a, b);
    cout << "a = " << a << endl;
    cout << "b = " << b << endl;
    //(2)显示指定类型
    double c = 3.14;
    double d = 6.28;
    mySwap<double>(c, d);
    cout << "c = " << c << endl;
    cout << "d = " << d << endl;
}

int main(){
    test01();
    system("pause");
    return 0;
}

image-20220226130337147

注意:

  1. 函数模板中,自动类型推导必须推导出一致的数据类型T,才可以使用

  2. 模板必须要求确定出T的数据类型,才可以使用(如下例中函数没有指出T的数据类型,调用时需要使用func<int>()

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

2.func template案例:数组排序

利用函数模板封装一个排序的函数,可以对不同数据类型的数组进行排序(排序规则从大到小,排序算法为选择排序)

#include<iostream>
using namespace std;

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

//2.mySort选择排序函数
template<typename T>
void mySort(T arr[], int len){
    for(int i = 0; i < len; ++i){
        //(1)认定最大值的下标
        int max = i;
        //(2)遍历数组中的元素尝试更新最大值max
        for(int j = i + 1; j < len; ++j){
            if(arr[max] < arr[j]) max = j;
        }
        //(3)如果认定的最大值与遍历后的最大值不相等(最大值max是否被更新),则交换max与i下标的元素值
        if(max != i) mySwap(arr[max], arr[i]);
    }
}

//3.printArray数组输出函数
template<typename T>
void printArray(T arr[], int len){
    for(int i = 0; i < len; ++i){
        cout << arr[i] << " ";
    }
    cout << endl;
}

void test01(){
    char charArr[] = "badcfe";
    int len = sizeof(charArr) / sizeof(char);
    mySort(charArr, len);
    printArray(charArr, len);
}

void test02(){
    int intArr[] = {7, 5, 4, 8, 10, 1, 6, 4};
    int len = sizeof(intArr) / sizeof(int);
    mySort(intArr, len);
    printArray(intArr, len);
}

int main(){
    test01();
    test02();
    system("pause");
    return 0;
}

image-20220227004004856

3.函数与函数模板的区别&调用规则:

普通函数与函数模板的区别:

  1. 普通函数调用时可以发生自动类型转换(隐式类型转换)
  2. 函数模板调用时(如果是利用自动类型推导来调用),不会发生隐式类型转换
  3. 函数模板调用时(如果是利用显示指定类型来调用),可以发生隐式类型转换
#include<iostream>
using namespace std;

//1.普通函数
int myAdd01(int a, int b){
    return a + b;
}

//2.函数模板
template<class T>
T myAdd02(T a, T b){
    return a + b;
}

void test01(){
    int a = 10;
    int b = 20;
    char c = 'c';
    //1.普通函数调用
    cout << myAdd01(a, b) << endl;
    cout << myAdd01(a, c) << endl;
    //2.函数模板自动类型推导调用
    cout << myAdd02(a, b) << endl;
    //cout << myAdd02(a, c) << endl;(报错不会发生隐式类型转换)
    //3.函数模板显示指定类型调用
    cout << myAdd02<int>(a, b) << endl;
    cout << myAdd02<int>(a, c) << endl;
}

int main(){
    test01();
    system("pause");
    return 0;
}

总结:建议使用显示指定类型的方式调用函数模板,可以自己确定通用类型T

普通函数与函数模板的调用规则:

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

注意:函数模板也可以发生重载

#include<iostream>
using namespace std;

void myPrint(int a, int b){
    cout << "调用的普通函数" << endl;
}

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

void test01(){
    cout << "test01:如果普通函数和函数模板都可以调用,则优先调用普通函数" << endl;
    int a = 10;
    int b = 10;
    myPrint(a, b);
}

void test02(){
    cout << "test02:如果普通函数和函数模板都可以调用,通过空模板参数列表来强制调用函数模板" << endl;
    int a = 10;
    int b = 20;
    myPrint<>(a, b);
}

void test03(){
    cout << "test03:如果函数模板可产生更好的匹配,则优先调用函数模板" << endl;
    char c1 = 'a';
    char c2 = 'b';
    myPrint(c1, c2);
}

int main(){
    test01();
    test02();
    test03();
    system("pause");
    return 0;
}

image-20220304201238716

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

4.func template的局限性:

当传入的T数据类型为数组、类(例如:Person)等数据类型时,template模板就无法使用了。

c++为了解决这种问题,使其提供的模板template可以实现重载,可以为这些特定的数据类型提供更具体的模板

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

class Person{
public:
    Person(string n, int a){
        this->name = n;
        this->age = a;
    }
    string name;
    int age;
};

template<typename T>
bool myCompare(T &sub1, T &sub2){
    if(sub1 == sub2){
        cout << "sub1 == sub2" << endl;
        return true;
    }else{
        cout << "sub1 != sub2" << endl;
        return false;
    }
}

//在函数头前添加template<>具体化Person的版本实现模板函数,具体化优先调用
template<> bool myCompare(Person &sub1, Person &sub2){
    if(sub1.name == sub2.name && sub1.age == sub2.age){
        cout << "sub1 == sub2" << endl;
        return true;
    }else{
        cout << "sub1 != sub2" << endl;
        return false;
    }
}

void test01(){
    int a = 10;
    int b = 20;
    myCompare(a, b);
}

void test02(){
    Person p1("Tom", 10);
    Person p2("John", 10);
    Person p3("Tom", 10);
    /*
        1.需要重载Person类型之间的比较运算符==
        2.再提供可以接收Person数据类型的函数模板
    */
    myCompare(p1, p2);
    myCompare(p1, p3);
}

int main(){
    test01();
    test02();
    system("pause");
    return 0;
}

image-20220304203745582

总结:

  1. 利用具体化的模板,可以解决自定义类型的通用化。
  2. 在STL中系统提供了大量的模板。

二、类模板

1.类模板基本使用:

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

template<typename T>
//后紧跟着类定义
函数模板说明
template声明创建模板
typename表明其后面的符号是一种数据类型,可以用class替代
T通用的数据类型,名称可以替换(通常为大写的字母)
#include<iostream>
using namespace std;

template<class NameType, class AgeType>
class Person{
public:
    Person(NameType n, AgeType a){
        this->name = n;
        this->age = a;
    }
    void showPerson(){
        cout << "name:" << this->name << "\tage:" << this->age << endl;
    }
    NameType name;
    AgeType age;
};

void test01(){
    Person<string, int> p1("lch", 20);
    p1.showPerson();
}

int main(){
    test01();
    system("pause");
    return 0;
}

image-20220305144236081

总结:类模板与函数模板语法非常相似,在声明模板template后面加类,称为模板类。

2.类模板与函数模板的区别:

  1. 类模板没有自动类型推导的使用方式(只有显示指定类型)。
    image-20220306163431094
  2. 类模板在模板参数列表中可以有默认参数(函数模板无法使用)。
    image-20220306164044870
#include<iostream>
using namespace std;

template<class NameType, class AgeType = int>
class Person{
public:
    Person(NameType n, AgeType a){
        this->name = n;
        this->age = a;
    }
    void showPerson(){
        cout << "name:" << this->name << "\tage:" << this->age << endl;
    }
    NameType name;
    AgeType age;
};

void test01(){
    //Person p1("lch", 20);//1.类模板没有自动类型推导的使用方式只有显示指定类型
    Person<string, int> p1("lch", 21);
    p1.showPerson();
    Person<string> p2("luochenhao", 21);//2.类模板在模板参数列表中可以有默认参数
    p2.showPerson();
}

int main(){
    test01();
    system("pause");
    return 0;
}

image-20220306164119820

3.class template中成员函数创建时机:

普通类中的成员函数开始就能够创建,类模板中的成员函数在被调用时才能够创建

#include<iostream>
using namespace std;

class Cock{
public:
    void showCock(){
        cout << "It's a Cock, bukbukbuk!" << endl;
    }
};

class Cat{
public:
    void showCat(){
        cout << "It's a Cat, miaomiaomiao~" << endl;
    }
};

template<class T>
class Animal{
public:
    T obj;
    void Cock(){
        obj.showCock();
    }
    void Cat(){
        obj.showCat();
    }
};

void test01(){
    Animal<Cock> animal1;
    animal1.Cock();
    //animal1.Cat();无法调用
    Animal<Cat> animal2;
    //animal2.Cock();无法调用
    animal2.Cat();
}

int main(){
    test01();
    system("pause");
    return 0;
}

image-20220306170329723

总结:

由于无法确定类模板T的数据类型(Cock or Cat),故类模板中的成员函数不会创建;

直到成员函数被调用时(数据类型已经确定),才能够创建成员函数。

4.class template对象做函数参数:

类模板实例化出的对象,向函数传递参数的方式有3种:

  1. 指定传入的类型(直接显示对象的数据类型)
  2. 参数模板化(将对象中的参数变为模板进行传递)
  3. 类模板化(将这个对象类型变为模板进行传递)
#include<iostream>
using namespace std;

template<class T1, class T2>
class Person{
public:
    Person(T1 n, T2 a){
        this->name = n;
        this->age = a;
    }
    void showPerson(){
        cout << "姓名:" << this->name << "\t年龄:" << this->age << endl;
    }
    T1 name;
    T2 age;
};

//1.指定传入类型
void printPerson1(Person<string, int> &p){
    p.showPerson();
}
void test01(){
    Person<string, int> p("lch", 21);
    printPerson1(p);
}

//2.参数模板化
template<class T1, class T2>
void printPerson2(Person<T1, T2> &p){
    p.showPerson();
    cout << "T1的类型为:" << typeid(T1).name() << endl;
    cout << "T2的类型为:" << typeid(T2).name() << endl;
}
void test02(){
    Person<string, int> p("lch", 21);
    printPerson2(p);
}

//3.整个类模板化
template<class T>
void printPerson3(T &p){
    p.showPerson();
    cout << "T的类型为:" << typeid(T).name() << endl;
}
void test03(){
    Person<string, int> p("lch", 21);
    printPerson3(p);
}

int main(){
    test01();
    test02();
    test03();
    system("pause");
    return 0;
}

image-20220306205307544

注意:指定参数传入的类型是使用最为广泛的一种传参方式。

5.class template与继承:

当类模板遇到继承问题时,应注意:

  1. 当子类继承父类时一个类模板时,子类在声明时需要指定出父类中T的类型(不指定则无法给子类分配内存)。
    image-20220306212850836
  2. 如果想灵活的指定出父类中T的类型,子类也需要变成类模板。
#include<iostream>
using namespace std;

//类模板与继承
template<class T>
class Base{
public:
    T m;
};

//1.class Son:public Base{};报错,必须要知道父类中T的数据类型才能继承给子类
class Son1:public Base<int>{};

//2.如果想灵活指定父类中T的类型,子类也需要变成类模板
template<class T1, class T2>
class Son2:public Base<T1>{
public:
    Son2(){
        cout << "T1的数据类型为:" << typeid(T1).name() << endl;
        cout << "T2的数据类型为:" << typeid(T2).name() << endl;
    }
    T2 obj;
};

int main(){
    Son1 s1;
    Son2<int, char> s2;
    system("pause");
    return 0;
}

image-20220306214416223

总结:如果父类是类模板,子类需要指定出父类中T的数据类型

6.class template成员函数类外实现:

类模板的成员函数的类内实现:

template<class T1, class T2>
class Person{
public:
    Person(T1 n, T2 a){
        this->name = n;
        this->age = a;
    }
    void showPerson(){
        cout << "name:" << this->name << "age:" << this->age << endl;
    }
    T1 name;
    T2 age;
};

类模板的成员函数的类外实现:

#include<iostream>
using namespace std;

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

//1.构造函数的类外实现
template<class T1, class T2>
Person<T1, T2>::Person(T1 n, T2 a){
    this->name = n;
    this->age = a;
}

//2.成员函数的类外实现
template<class T1, class T2>
void Person<T1, T2>::showPerson(){
    cout << "name:" << this->name << "\tage:" << this->age << endl;
}

int main(){
    Person<string, int> p("lch", 21);
    p.showPerson();
    system("pause");
    return 0;
}

image-20220306215554951

注意:类模板中的成员函数类外实现时,需要加上模板参数列表,例void Person<T1, T2>::showPerson()

7.class template成员函数分文件编写:

类模板中成员函数创建时机是在调用阶段,导致分文件编写时链接不到

image-20220306234511667

解决方案有2种:

  1. 方法1:直接包含cpp源文件(使用较少)
    image-20220306234931584
  2. 方法2:将.h声明和.cpp实现写到同一个文件中,并更改后缀名为hpp(hpp是约定的名称/非强制)

image-20220306235620654

#ifndef PERSON_H
#define PERSON_H
#pragma once
#include <iostream>
using namespace std;

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

//1.构造函数的类外实现
template<class T1, class T2>
Person<T1, T2>::Person(T1 n, T2 a){
    this->name = n;
    this->age = a;
}

//2.成员函数的类外实现
template<class T1, class T2>
void Person<T1, T2>::showPerson(){
    cout << "name:" << this->name << "\tage:" << this->age << endl;
}

#endif // PERSON_H
#include <iostream>
#include "Person.hpp"
using namespace std;

int main(){
    Person<string, int> p("lch", 18);
    p.showPerson();
    system("pause");
    return 0;
}

image-20220306235542257

程序成功运行

8.类模板与友元:

实现类模板配合友元函数类内和类外实现

  • 全局函数类内实现:直接在类内声明友元
  • 全局函数类外实现:需要提前让编译器知道全局函数的存在(重点)
#include<iostream>
using namespace std;

//通过全局函数打印Person信息
template<class T1, class T2>
class Person{
    //全局函数类内实现
    friend void printPerson(Person<T1, T2> p){
        cout << "name:" << p.name << "age:" << p.age << endl;
    }
public:
    Person(T1 n, T2 a){
        this->name = n;
        this->age = a;
    }
private:
    T1 name;
    T2 age;
};

int main(){
    Person<string, int> p("lch", 21);
    printPerson(p);
    system("pause");
    return 0;
}
#include<iostream>
using namespace std;

template<class T1, class T2>
class Person;

//全局函数类外实现
template<class T1, class T2>
void printPerson(Person<T1, T2> p){
    cout << "name:" << p.name << "\tage:" << p.age << endl;
}

template<class T1, class T2>
class Person{
    //全局函数类外实现(加一个空模板的参数列表)
    friend void printPerson<>(Person<T1, T2> p);
public:
    Person(T1 n, T2 a){
        this->name = n;
        this->age = a;
    }
private:
    T1 name;
    T2 age;
};

int main(){
    Person<string, int> p("lch", 21);
    printPerson(p);
    system("pause");
    return 0;
}

image-20220307135549431

三、通用数组类★

1.功能分析:

  1. 可以对内置数据类型以及自定义数据类型的数据进行存储
  2. 将数组中的数据存储到堆区
  3. 构造函数中可以传入数组的容量
  4. 提供对应的拷贝构造函数以及operator=防止浅拷贝问题

image-20220307140618264

2.功能实现:

image-20220310170705079

#ifndef MYARRAY_H
#define MYARRAY_H
#pragma once
#include <iostream>
using namespace std;

template<class T>
class MyArray{
    public:
    	//实现有参构造
        MyArray(int capa){
            cout << "MyArray有参构造调用" << endl;
            this->capacity = capa;
            this->size = 0;
            this->pAddress = new T[this->capacity];
        }
        //防止浅拷贝问题需要重写拷贝构造函数
        MyArray(const MyArray &arr){
            cout << "MyArray拷贝构造调用" << endl;
            this->capacity = arr.capacity;
            this->size = arr.size;
            //this->pAddress = arr.pAddress;指针不能这样直接赋值,会导致浅拷贝问题堆区内存重复释放
            //(1)重新在堆区开辟一个数组空间,让指针进行维护
            this->pAddress = new T[arr.capacity];
            //(2)将arr中原有的数据拷贝到新开辟的空间中
            for(int i = 0; i < this->size; ++i) this->pAddress[i] = arr.pAddress[i];
        }
        //重载operator=防止浅拷贝问题
        MyArray& operator=(const MyArray &arr){
            cout << "MyArray的operator=调用" << endl;
            //(1)先判断原来堆区是否有数据,如果有先释放
            if(this->pAddress != NULL){
                delete[] this->pAddress;
                this->pAddress = NULL;
                this->capacity = 0;
                this->size = 0;
            }
            //(2)深拷贝
            this->capacity = arr.capacity;
            this->size = arr.size;
            this->pAddress = new T[arr.capacity];
            for(int i = 0; i < this->size; ++i) this->pAddress[i] = arr.pAddress[i];
            return *this;
        }
        //堆区开辟的数据需要析构函数手动释放
        ~MyArray(){
            if(this->pAddress != NULL){
                cout << "MyArray析构函数调用" << endl;
                delete[] this->pAddress;//清空数组中的数据
                this->pAddress = NULL;//将指针置空防止野指针
            }
        }
    private:
        T *pAddress;//指针指向堆区开辟的真实数组
        int capacity;//数组容量
        int size;//数组元素个数
};

#endif // MYARRAY_H
#include <iostream>
#include "MyArray.hpp"
using namespace std;

void test01(){
    MyArray<int> arr1(10);//测试有参构造功能
    MyArray<int> arr2(arr1);//测试拷贝构造功能
    MyArray<int> arr3(100);
    arr3 = arr1;//测试=运算符重载
}

int main(){
    test01();
    system("pause");
    return 0;
}

image-20220310170743544

3.功能增加&程序修改:

  1. 提供尾插法尾删法对数组中的数据进行增加和删除。
  2. 可以通过下标的方式访问数组中的元素(重载数组中的[]实现访问操作)。
  3. 可以获取数组中当前元素个数数组容量
#ifndef MYARRAY_H
#define MYARRAY_H
#pragma once
#include <iostream>
using namespace std;

template<class T>
class MyArray{
    public:
        //1.实现有参构造
        MyArray(int capa){
            this->capacity = capa;
            this->size = 0;
            this->pAddress = new T[this->capacity];
        }
        //2.防止浅拷贝问题需要重写拷贝构造函数
        MyArray(const MyArray &arr){
            this->capacity = arr.capacity;
            this->size = arr.size;
            //this->pAddress = arr.pAddress;指针不能这样直接赋值,会导致浅拷贝问题堆区内存重复释放
            //(1)重新在堆区开辟一个数组空间,让指针进行维护
            this->pAddress = new T[arr.capacity];
            //(2)将arr中原有的数据拷贝到新开辟的空间中
            for(int i = 0; i < this->size; ++i) this->pAddress[i] = arr.pAddress[i];
        }
        //3.重载operator=防止浅拷贝问题
        MyArray& operator=(const MyArray &arr){
            //(1)先判断原来堆区是否有数据,如果有先释放
            if(this->pAddress != NULL){
                delete[] this->pAddress;
                this->pAddress = NULL;
                this->capacity = 0;
                this->size = 0;
            }
            //(2)深拷贝
            this->capacity = arr.capacity;
            this->size = arr.size;
            this->pAddress = new T[arr.capacity];
            for(int i = 0; i < this->size; ++i) this->pAddress[i] = arr.pAddress[i];
            return *this;
        }
        //4.尾插法实现数据增加
        void Push_Back(const T &val){
            //(1)判断容量已经满
            if(this->capacity == this->size) return;
            //(2)进行数据插入
            this->pAddress[this->size] = val;
            this->size++;
        }
        //5.尾删法实现数据删除
        void Pop_Back(){
            //(1)判断容量已经空
            if(this->size == 0) return;
            //(2)进行数据删除
            this->size--;
        }
        //6.用户通过下标的方式访问数组中的元素(如果函数调用想作为等号的左值存在arr[0]=100;需要返回T&)
        T& operator[](int index){
            return this->pAddress[index];
        }
        //7.返回数组的容量
        int getCapacity(){
            return this->capacity;
        }
        //8.返回数组的个数
        int getSize(){
            return this->size;
        }
        //9.堆区开辟的数据需要析构函数手动释放
        ~MyArray(){
            if(this->pAddress != NULL){
                delete[] this->pAddress;//清空数组中的数据
                this->pAddress = NULL;//将指针置空防止野指针
            }
        }
    private:
        T *pAddress;//指针指向堆区开辟的真实数组
        int capacity;//数组容量
        int size;//数组元素个数
};

#endif // MYARRAY_H
#include <iostream>
#include "MyArray.hpp"
using namespace std;

void printArray(MyArray<int> &arr){
    cout << ">>>数组内容打印如下:" << endl;
    for(int i = 0; i < arr.getSize(); ++i){
        cout << arr[i] << " ";
    }
    cout << endl;
}

void test01(){
    //arr1数组测试
    MyArray<int> arr1(10);
    for(int i = 0; i < 7; ++i) arr1.Push_Back(i);
    printArray(arr1);
    cout << "数组的容量为:" << arr1.getCapacity() << endl;
    cout << "数组中的元素个数为:" << arr1.getSize() << endl;
    //arr2数组测试
    MyArray<int> arr2(arr1);
    arr2.Pop_Back();
    printArray(arr2);
    cout << "数组的容量为:" << arr2.getCapacity() << endl;
    cout << "数组中的元素个数为:" << arr2.getSize() << endl;
}

class Person{
public:
    Person(){};
    Person(string n, int a){
        this->name = n;
        this->age = a;
    }
    string name;
    int age;
};

void printPersonArray(MyArray<Person> &arr){
    cout << ">>>数组内容打印如下:" << endl;
    for(int i = 0; i < arr.getSize(); ++i){
        cout << "姓名:" << arr[i].name << "\t年龄:" << arr[i].age << endl;
    }
}

void test02(){
    MyArray<Person> arr(10);
    Person p1("lch", 21);
    Person p2("cxk", 18);
    Person p3("kfc", 28);
    Person p4("gkd", 21);
    arr.Push_Back(p1);
    arr.Push_Back(p2);
    arr.Push_Back(p3);
    arr.Push_Back(p4);
    printPersonArray(arr);
    cout << "数组的容量为:" << arr.getCapacity() << endl;
    cout << "数组中的元素个数为:" << arr.getSize() << endl;
}

int main(){
    test01();
    test02();
    system("pause");
    return 0;
}

image-20220310175313495

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

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

相关文章

AOA估计中的MUSIC算法(Matlab代码实现)

目录 &#x1f4a5;1 概述 &#x1f4da;2 运行结果 &#x1f389;3 参考文献 &#x1f468;‍&#x1f4bb;4 Matlab代码 &#x1f4a5;1 概述 随着阵列信号处理技术的不断发展,到达角估计(Angle Of Arrival)的研究在移动通信系统中具有重要意义。通过分析经典MUSIC算法,…

golang 自定义命令行flag包简单使用

一、为什么需要使用golang自定义命令行 不恰当的比喻&#xff0c;当我们写了一个服务代码后&#xff0c;按照简单的思维&#xff0c;我们会在业务代码中将要连接的数据库 用户名、主机名、端口号、密码写死。 那么也就意味着我们启动该服务后都只能固定连接某一个数据库&#x…

etcd快速入门

etcd是什么 etcd是CoreOS团队于2013年6月发起的开源项目&#xff0c;它的目标是构建一个高可用的分布式键值(key-value)数据库。 etcd内部采用raft协议作为一致性算法&#xff0c;etcd基于Go语言实现。 etcd作为服务发现系统&#xff0c;有以下的特点&#xff1a; 1.简单&#…

分享67个PHP源码,总有一款适合您

链接&#xff1a;https://pan.baidu.com/s/1MzKN0bLDRv0i290R2erMHQ?pwdbo2i 提取码&#xff1a;bo2i PHP源码 分享67个PHP源码&#xff0c;总有一款适合您 下面是文件的名字&#xff0c;我放了一些图片&#xff0c;文章里不是所有的图主要是放不下...&#xff0c;大家下载…

寒假每日一题W1D3——上课睡觉

题目描述 有 N 堆石子&#xff0c;每堆的石子数量分别为 a1,a2,…,aN。 你可以对石子堆进行合并操作&#xff0c;将两个相邻的石子堆合并为一个石子堆&#xff0c;例如&#xff0c;如果 a[1,2,3,4,5]&#xff0c;合并第 2,3 堆石子&#xff0c;则石子堆集合变为 a[1,5,4,5]。…

【学习】backdoor attacks、Adversarial Attack on Images、Adversarial Attack on Audio

文章目录一、后门攻击backdoor attacks1、data poisoning2、backdoored PLM3、defenseONION4、后门攻击:绕过ONION防御5、摘要二、Adversarial Attack on Imagesone pixel attackdifferential evolution三、Adversarial Attack on Audio一、后门攻击backdoor attacks 什么是后…

进程的终止和等待

目录 进程终止 如何获取退出码呢&#xff1f; 进程退出方法有哪些&#xff1f; 对于进程退出&#xff0c;内核OS做了什么&#xff1f; 进程等待 为什么要进行进程等待&#xff1f; 如何进行进程等待&#xff1f; 什么是阻塞和非阻塞等待&#xff1f; 进程终止 对于进程…

飞依诺冲刺科创板上市:上半年出现亏损,因商业秘密纠纷被起诉

近日&#xff0c;飞依诺科技股份有限公司&#xff08;下称“飞依诺”&#xff09;在上海证券交易所递交招股书&#xff0c;准备在科创板上市。本次冲刺上市&#xff0c;飞依诺计划募资11.22亿元&#xff0c;将用于生产基地升级项目、新产品研发与总部基地建设项目、营销网络建设…

【Linux】一文掌握Linux基本指令(下)

本章命令大致总结命令功能cat打印文件内容echo打印文件内容> 输出重定向 >>追加重定向< 输入重定向 more 查看文本内容 less等价于morehead打印文本前n行tail 打印文本后n行 |管道date时间相关cal日历sort文本排序uniq相邻文本降重zip打包压缩unzip解包tar打包/解包…

蓝桥杯寒假集训第四天(全球变暖DFS)

没有白走的路&#xff0c;每一步都算数&#x1f388;&#x1f388;&#x1f388; 题目描述&#xff1a; 有一个正方形区域&#xff0c;里面有大陆和海洋&#xff0c;暂且用‘.’表示海洋&#xff0c;用‘#’表示大陆。我们把上下左右都连在一起的大陆称之为岛屿。但是随着气温…

07 来自于网友的 retrieveFileStream 的一个问题, 导致系统程序异常

前言 可以先参考前面一篇文章 retrieveFileStream 之后需要调用 completePendingCommand 否则业务代码会存在问题 retrieveFileStream 之后需要调用 completePendingCommand 否则业务代码会存在问题 这里的问题 主要是来自于 某 qq 交流群的网友 呵呵 当然 这里测试用例代码…

新鲜速递:Spring Data JPA 3.0快速入门、进阶到精通

第一章、安装Spring Data JPA 第一步&#xff0c;先确保你使用的是Spring Boot 3.0或以上环境&#xff0c;可以在pom.xml里加入Spring Data JPA依赖 <dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-data-…

【ROS】—— ROS通信机制——话题通信(二)

文章目录前言1. 话题通信1.1 话题通讯理论模型1.2 话题通信基本操作&#xff08;C&#xff09;1.2.1 简单发布框架的实现1.2.2 发布逻辑的实现1.2.3 订阅方的实现1.3 话题通信基本操作&#xff08;python&#xff09;1.3.1 发布的实现1.3.2 订阅的实现1.4 话题通信自定义msg1.4…

五问补盲(五)| 想要长得好看又好用,补盲激光雷达应该怎么做?

上期&#xff0c;我们聊了好用的补盲激光雷达&#xff0c;得满足哪些条件&#xff1f; 好用是必备素质&#xff0c;属于补盲激光雷达的底线。好用之外&#xff0c;补盲激光雷达还有一种更直观的竞争力&#xff0c;那就是——外型。 有句话说的好&#xff0c;很多时候&#xff0…

2022跟学尚硅谷Maven入门(二)IDEA操作

2022跟学尚硅谷Maven入门二 IDEA操作第四章 使用Maven&#xff1a;IDEA环境第一节 创建父工程1.创建 Project2.开启自动导入第二节 配置 Maven 信息第三节 创建 Java 模块工程第四节 创建Web模块工程1.创建模块2.修改打包方式3.Web 设定4.借助IDEA生成web.xml5.设置 Web 资源的…

数据库,计算机网络、操作系统刷题笔记21

数据库&#xff0c;计算机网络、操作系统刷题笔记21 2022找工作是学历、能力和运气的超强结合体&#xff0c;遇到寒冬&#xff0c;大厂不招人&#xff0c;可能很多算法学生都得去找开发&#xff0c;测开 测开的话&#xff0c;你就得学数据库&#xff0c;sql&#xff0c;oracle…

基于thinkphp6搭建的 admin后台管理基础框架,方便快速进行二次开发

小牛Admin v2.1 完整代码下载地址&#xff1a;基于thinkphp6搭建的 admin后台管理基础框架 使用thinkphp6 layui 搭建的 admin后台管理基础框架&#xff0c;方便快速进行二次开发 该项目是在 http://www.xnadmin.cn/ 小牛Admin 开源项目的基础上进行个人优化的产物 运行环境…

Redis-用户签到UV统计

一、用户签到 1.1 BitMap用法 我们按月来统计用户签到信息&#xff0c;签到记录为1,未签到记录为0 把每一个bit位对应当月的每一天&#xff0c;形成了映射关系。用0和1表示业务状态&#xff0c;这种思路就称为位。Redis中是用利用string类型数据结构实现BitMap&#xff0c;因…

使用pip命令时,报错:_sysconfigdata_x86_64_conda_cos7_linux_gnu.py

问题&#xff1a; 在linux服务器中想使用pip命令pip show list查看安装了哪些包时&#xff0c;报错 ModuleNotFoundError: No module named ‘_sysconfigdata_x86_64_conda_cos7_linux_gnu’ 问题原因 原因是&#xff1a;在当前的环境下的python中丢失了一个备份文件&#xf…

经典算法之常用排序

目录❤️前言&#x1f498;一、分治思想&#x1f49e;二、归并排序1.实现方法2.动图分析3.代码模板&#x1f496;三、快速排序1.实现方法2.动图分析3.代码模板❤️前言 本文介绍两种基于分治思想的经典排序算法&#xff1a; 归并排序与快速排序 &#x1f498;一、分治思想 分…