从0到1入门C++编程——08 函数模板和类模板

文章目录

  • 函数模板
    • 1.函数模板基本语法
    • 2.函数模板使用的注意事项
    • 3.函数模板案例——数组排序
    • 4.普通函数和函数模板的区别
    • 5.普通函数和函数模板的调用规则
    • 6.模板的局限性
  • 类模板
    • 1.类模板
    • 2.类模板和函数模板的区别
    • 3.类模板中成员函数创建时机
    • 4.类模板对象做函数参数
    • 5.类模板与继承
    • 6.类模板成员函数的类外实现
    • 7.类模板分文件编写
    • 8.类模板与友元
  • 类模板案例

函数模板

C++是面向对象的编程,其另一种编程思想是泛型编程,主要利用的技术就是模板。C++提供两种模板机制:函数模板和类模板。
模板是建立通用的模具,能够大大提高复用性。
模板的特点:模板的通用性很强,但其不是万能的;模板不可以直接使用,其只是一个框架。
比如照片模板需要把自己的照片P上去,PPT模板中需要把自己的内容写进去。

1.函数模板基本语法

函数模板的作用:建立一个通用函数,函数返回值类型和形参类型可以不具体制定,用一个虚拟的类型来代表。
函数模板基本语法:template< typename T > 后面是函数的声明或者实现
template 声明创建模板;
typename 后面的符号是一种数据类型,typename可以用class代替;
T 表示通用的数据类型,其名称可以替换,一般是大写字母。
函数模板在什么时候使用呢,下面两个函数分别实现交换两个整型数和交换两个浮点型数。

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是一个通用的数据类型,提示编译器不要报错
void Swap(T &a,T &b)
{T temp = a;a = b;b = temp;
}

在调用的时候有两种方式,一种是自动类型推导,另一种是显式指定类型。

int a=10;
int b=20;
double c=0.1;
double d=0.2;Swap(a,b);   //自动类型推导
Swap(c,d);
Swap<int>(a,b);  //显式指定类型
Swap<double>(c,d);

2.函数模板使用的注意事项

函数模板使用的注意事项:
1.自动类型推导中,必须推导出一致的数据类型T才可以使用;
2.模板必须要确定出T的数据类型,才可以使用。
上面的交换例子中,传入的两个参数类型必须一致,否则无法推导出一致的数据类型,传入不同类型的参数代码会报错。
模板必须要确定出T的数据类型,才可以使用。

template <typename T>
void fun()
{cout<<"fun()的调用!"<<endl;
}int main()
{fun();   //直接调用时无法确定T的数据类型,尽管T没有在函数中使用,代码会报错fun<int>();   //随便给T显式的指定数据类型,这样调用就是正确的...
}

3.函数模板案例——数组排序

利用函数模板,对不同类型的数组进行排序。
该案例的代码如下。

#include <iostream>
#include <string>
using namespace std;template <typename T>  //声明一个模板,T是一个通用的数据类型,提示编译器不要报错
void Swap(T &a,T &b)
{T temp = a;a = b;b = temp;
}template <class T> 
void arraySort(T arr,int len)   
{for(int i=0;i<len;i++)  //选择排序{int min=i;for(int j=i+1;j<len;j++){if(arr[min]>arr[j]){min=j;}}if(min!=i){Swap(arr[min],arr[i]);}}
}template <class T> 
void printArr(T arr,int len)
{for(int i=0;i<len;i++){cout<<arr[i];}cout<<endl;
}int main()
{int a[10]={1,5,9,3,6,2,4,8,7,0};char b[]="bcade";int len_a=sizeof(a)/sizeof(a[0]);int len_b=strlen(b)/sizeof(b[0]);cout<<"排序前整型数组a: ";printArr(a,len_a);arraySort(a,len_a);cout<<"排序后整型数组a: ";printArr(a,len_a);cout<<"排序前字符型数组b: ";printArr(b,len_b);arraySort(b,len_b);cout<<"排序后字符型数组b: ";printArr(b,len_b);system("pause");return 0;
}

代码中分别对字符型数组和整型数组进行了测试,运行结果如下图所示。
在这里插入图片描述

4.普通函数和函数模板的区别

普通函数调用时可以发生自动类型转换,即隐式类型转换。
函数模板调用时,如果利用自动类型推导,不会发生隐式类型转换;如果利用显示指定类型的方式,可以发生隐式类型转换。

#include <iostream>
#include <string>
using namespace std;int add(int a,int b)
{return a+b;
}template <typename T>
T t_add(T a,T b)
{return a+b;
}int main()
{int a=10;int b=20;char c='a';cout<<"1.a+b="<<add(a,b)<<endl;cout<<"1.a+c="<<add(a,c)<<endl;cout<<"2.a+b="<<t_add(a,b)<<endl;//cout<<"2.a+b="<<t_add(a,c)<<endl;   //报错cout<<"2.a+b="<<t_add<int>(a,c)<<endl;   //需要显式的指定system("pause");return 0;
}

程序运行结果如下图所示。
在这里插入图片描述
因此,在使用函数模板时,一般使用显式的方式调用函数模板,因为在调用的时候,我们就可以知道具体的函数类型T。

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

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

#include <iostream>
using namespace std;void fun(int a,int b)
{cout<<"1.调用普通函数!"<<endl;
}template <typename T>
void fun(T a,T b)
{cout<<"2.调用函数模板!"<<endl;
}template <typename T>
void fun(T a,T b,T c)   //函数模板的重载
{cout<<"3.调用重载的函数模板!"<<endl;
}int main()
{int a=10;int b=20;fun(a,b);   //优先调用普通函数fun<>(a,b);   //如果非要调用函数模板,通过空模板参数列表来实现fun(a,b,30);   //调用重载的函数模板char c = 'a';char d = 'b';fun(c,d);   //调用普通函数需要发生隐式类型转换,函数模板有更好的匹配性system("pause");return 0;
}

上面程序的运行结果如下图所示。
在这里插入图片描述

6.模板的局限性

模板并不是万能的,有些特定的数据类型,需要用具体化的方式做特殊实现。
利用具体化的模板,可以解决自定义类型的通用化。
学习模板并不是为了写模板,而是在STL(Standard Template Library, 标准模板库)能够运用系统提供的模板。
比如下面的代码,如果要比较特定的数据类型,就需要用具体化的方式在代码中做特殊实现。

#include <iostream>
#include <string>
using namespace std;class Person
{
public:Person(string name,int age){this->name = name;this->age = age;}string name;int age;
};template <class T>
void Compare(T &a,T &b)
{if(a==b){cout<<"a=b"<<endl;}else{cout<<"a!=b"<<endl;}
}template<> void Compare(Person &p1,Person &p2)  //模板的重载,具体化的数据类型会优先调用
{if(p1.name==p2.name && p1.age==p2.age){cout<<"p1=p2"<<endl;}else{cout<<"p1!=p2"<<endl;}
}int main()
{Person p1("Tom",10);Person p2("Tom",10);Compare(p1,p2);system("pause");return 0;
}

类模板

1.类模板

类模板和函数模板写法相似,稍有不同的是,类中含有的数据类型较多,这就需要在声明类模板的时候定义多个通用类型,然后在调用的时候传入具体的数据类型。
一个简单的类模板例子如下。

#include <iostream>
#include <string>
using namespace std;template <class NameT,class AgeT>   //类模板,有多个类型的情况
class Person
{
public:Person(NameT name,AgeT age){this->name = name;this->age = age;}void showinfo(){cout<<"name:"<<name<<endl;cout<<"age:"<<age<<endl;}NameT name;AgeT age;
};int main()
{Person<string,int> p("Tom",10);p.showinfo();system("pause");return 0;
}

程序运行结果如下图所示。
在这里插入图片描述

2.类模板和函数模板的区别

类模板和函数模板的区别:类模板中没有自动类型推导的使用方式,只能用显示指定类型的方式;类模板在模板参数列表中可以有默认参数。

Person p("Tom",10);  //报错,无法进行自动类型推导
Person<string,int> p("Tom",10);

如果在声明类模板的时候给了默认参数,在调用的时候就可以省略掉该位置的数据类型。

template <class NameT,class AgeT=int> 
Person<string> p("Tom",10);

3.类模板中成员函数创建时机

普通类中的成员函数一开始就可以创建,类模板中的成员函数在调用时才创建。
因为类模板中的成员函数类型是不确定的,只有在调用的时候才可以确定,因此,类模板中的成员函数在调用时才创建。
类模板中成员函数创建时机的例子如下。

#include <iostream>
#include <string>
using namespace std;class Person1
{
public:void showPerson1(){cout<<"调用showPerson1()成员函数!"<<endl;}
};class Person2
{
public:void showPerson2(){cout<<"调用showPerson2()成员函数!"<<endl;}
};template <class T>
class Myclass
{
public:T obj;void fun1(){obj.showPerson1(); //不会报错,因为T的类型还不确定}void fun2(){obj.showPerson2();}
};int main()
{Myclass<Person1> p1;p1.fun1();   //成员函数在调用时才创建Myclass<Person2> p2;p2.fun2();   //成员函数在调用时才创建system("pause");return 0;
}

程序运行结果如下图所示。
在这里插入图片描述

4.类模板对象做函数参数

通过类模板创建的对象,有三种方式向函数传参:直接指定传入类型;参数模板化;整个类模板化。直接指定传入类型是比较常用的方式。
如果要看模板中某个通用数据类型T具体是什么类型,可以使用以下语句实现。

typeid(T).name()

类模板对象做函数参数的例子如下。

#include <iostream>
#include <string>
using namespace std;template <class NameT,class AgeT>
class Person
{
public:Person(NameT name,AgeT age){this->name = name;this->age = age;}void showinfo(){cout<<"name:"<<name<<endl;cout<<"age:"<<age<<endl;}NameT name;AgeT age;
};//1.指定传入类型
void fun1(Person<string,int> &p)
{p.showinfo();
}//2.参数模板化
template <class T1,class T2>
void fun2(Person<T1,T2> &p)
{p.showinfo();cout<<"T1的类型为:"<<typeid(T1).name()<<endl;cout<<"T2的类型为:"<<typeid(T2).name()<<endl;
}//3.整个类模板化
template <class T>
void fun3(T &p)
{p.showinfo();cout<<"T的类型为:"<<typeid(T).name()<<endl;
}int main()
{Person<string,int> p1("Tom",10);fun1(p1);Person<string,int> p2("Brown",11);fun2(p2);Person<string,int> p3("Jack",12);fun3(p3);system("pause");return 0;
}

程序运行结果如下图所示。
在这里插入图片描述

5.类模板与继承

当类模板遇到继承的时候, 需要注意的是:当子类继承的父类是一个类模板时,子类在声明的时候,需要指定父类中的T类型
如果不指定父类中的T类型,编译器无法给子类分配内存,如果想要灵活指定出父类中的T类型,子类也需要写成类模板。

template <class T>
class Parent
{
public:T a;
};//class Son : public Parent   //不指明父类中的T类型会报错
class Son : public Parent <int>  //需指明父类中的T类型才能完成继承
{};

如果要灵活指定父类中的T类型,子类也需要变成类模板。

#include <iostream>
#include <string>
using namespace std;template <class T>
class Parent
{
public:T a;
};template <class T1,class T2>  //要灵活指定父类中的T类型,子类需要变成类模板
class Son : public Parent <T1>
{
public:Son(){cout<<"T1的类型为:"<<typeid(T1).name()<<endl;cout<<"T2的类型为:"<<typeid(T2).name()<<endl;}T2 b;
};void fun()
{Son<int,char> s;  //父类传入的是int,子类传入的是char
}int main()
{fun();system("pause");return 0;
}

上面程序的运行结果如下图所示。
在这里插入图片描述

6.类模板成员函数的类外实现

前面已经提到过类模板成员函数的类内实现,一个简单的例子如下。

#include <iostream>
#include <string>
using namespace std;template <class T1,class T2>
class Person
{
public:Person(T1 name,T2 age){this->name = name;this->age = age;}void showinfo(){cout<<"name:"<<name<<endl;cout<<"age:"<<age<<endl;}T1 name;T2 age;
};void fun()
{Person<string,int> p("Tom",10);p.showinfo();
}int main()
{fun();system("pause");return 0;
}

同样是上面的例子,类模板成员函数的类外实现如下。

#include <iostream>
#include <string>
using namespace std;template <class T1,class T2>
class Person
{
public:Person(T1 name,T2 age);  //构造函数声明void showinfo();  //成员函数声明T1 name;T2 age;
};template <class T1,class T2>   //先定义模板
Person<T1,T2>::Person(T1 name,T2 age)  //类模板构造函数的类外实现
{this->name = name;this->age = age;
}template <class T1,class T2>  //类模板成员函数的类外实现
void Person<T1,T2>::showinfo()
{cout<<"name:"<<name<<endl;cout<<"age:"<<age<<endl;
}void fun()
{Person<string,int> p("Tom",10);p.showinfo();
}int main()
{fun();system("pause");return 0;
}

7.类模板分文件编写

类模板中成员函数的创建时机在调用阶段,这会导致代码分文件编写时链接不到。
解决的方法:直接包含.cpp源文件;将声明和实现写在同一个文件中,并将后缀名改为.hpp。
比如上面的例子,将其拆分成三个文件,person.h中的内容如下。

#pragma once
#include <iostream>
using namespace std;template <class T1,class T2>
class Person
{
public:Person(T1 name,T2 age);void showinfo();T1 name;T2 age;
};

person.cpp中的内容如下。

#include "person.h"template <class T1,class T2>
Person<T1,T2>::Person(T1 name,T2 age)
{this->name = name;this->age = age;
}template <class T1,class T2>
void Person<T1,T2>::showinfo()
{cout<<"name:"<<name<<endl;cout<<"age:"<<age<<endl;
}

demo.cpp中的内容如下。

#include <iostream>
#include <string>
using namespace std;
#include "person.h"
//#include "person.cpp"  //将头文件替换为源文件后程序就能正确运行了void fun()
{Person<string,int> p("Tom",10);p.showinfo();
}int main()
{fun();system("pause");return 0;
}

这样写看似没有问题,但是在运行程序的时候发生了错误,错误内容如下。
在这里插入图片描述
解决错误的第一种方法就是保持原有的文件数量不变,但是将引用的头文件替换为源文件,这样在编译源文件的时候也会用到头文件。
第二种方法是将头文件和源文件的内容合在一个文件中,函数的声明和实现在同一个文件中实现,然后引用这个头文件即可,为了区分其是类模板,后缀名可改为.hpp,但也可以不改动。

8.类模板与友元

全局函数类模板内实现时,直接在类模板内声明友元即可。全局函数类模板外实现时,需要提前让编译器知道全局函数的存在。
全局函数类内实现的例子如下。

#include <iostream>
#include <string>
using namespace std;template <class T1,class T2>
class Person
{friend void showinfo(Person<T1,T2> p) //通过友元 类内实现{cout<<"name:"<<p.name<<endl;cout<<"age:"<<p.age<<endl;}
public:Person(T1 name,T2 age){this->name = name;this->age = age;}
private:T1 name;T2 age;
};void fun()
{Person<string,int> p("Tom",10);showinfo(p);
}int main()
{fun();system("pause");return 0;
}

全局函数类外实现的例子如下。

#include <iostream>
#include <string>
using namespace std;//提前声明类模板 
template <class T1,class T2>
class Person;//全局函数提到类前面
template <class T1,class T2>
void showinfo(Person<T1,T2> p)  //通过友元 类外实现
{cout<<"name:"<<p.name<<endl;cout<<"age:"<<p.age<<endl;
}template <class T1,class T2>
class Person
{friend void showinfo<>(Person<T1,T2> p);  //声明的时候要加上空模板参数列表
public:Person(T1 name,T2 age){this->name = name;this->age = age;}
private:T1 name;T2 age;
};void fun()
{Person<string,int> p("Tom",10);showinfo(p);
}int main()
{fun();system("pause");return 0;
}

类模板案例

实现一个通用的数组类,要求如下:
可以对内置数据类型以及自定义数据类型的数据进行存储;
将数组中的数据存储到堆区;
构造函数中可以传入数组的容量;
提供对应的拷贝构造函数以及operator=防止浅拷贝问题;
提供尾插法和尾删法对数组中的数据进行增加和删除;
可以通过下标的方式访问数组中的元素;
可以获取数组中当前元素个数和数组的容量。
MyArray.hpp文件中的代码如下,其中包括构造函数、析构函数和一些函数的重载。

#pragma once 
#include <string>
using namespace std;template <class T>
class MyArray
{
public:MyArray(int capacity)  //带参构造函数{cout<<"构造函数调用"<<endl;this->capacity = capacity;this->size = 0;this->p = new T[this->capacity];  //根据传入的数组容量大小开辟堆区内存}MyArray(const MyArray &a)  //拷贝构造函数{cout<<"拷贝构造函数调用"<<endl;this->capacity = a.capacity;this->size = a.size;//this->p = a.p;  //浅拷贝this->p = new T[a.capacity];  //深拷贝for(int i=0;i<this->size;i++)  //将原来数组中的数据进行拷贝{this->p[i] = a.p[i];}}MyArray& operator=(const MyArray &a){cout<<"operator=函数调用"<<endl;if(this->p!=NULL)  //先判断原来堆区中是否有数据,如果有就先释放{delete [] this->p;this->p = NULL;this->capacity = 0;this->size = 0;}//进行深拷贝this->capacity = a.capacity;this->size = a.size;this->p = new T[a.capacity];for(int i=0;i<this->size;i++){this->p[i] = a.p[i];}return *this;}void Insert(const T &a)  //尾插法{if(this->size == this->capacity){cout<<"数组容量已满!"<<endl;return;}this->p[this->size] = a;  //在数组尾插入数据this->size++;  //更新当前数组大小}void Delete()  //尾删法{if(this->size == 0){cout<<"数组已空!"<<endl;return;}this->size--;  //让用户访问不到即可}T& operator[](int index)  //通过下标的方式访问数组元素{return this->p[index];}int getCap()  //返回数组容量{return this->capacity;}int getSize()  //返回数组大小{return this->size;}~MyArray()  //析构函数{cout<<"析构函数调用"<<endl;if(this->p!=NULL){delete [] this->p;this->p = NULL;}}
private:T *p;  //指向堆区开辟的数组int capacity;  //数组容量int size;   //数组大小
};

主函数文件中的代码如下。

#include <iostream>
#include <string>
using namespace std;
#include "MyArray.hpp"void fun1()  //各函数的调用测试
{MyArray<int> a(5);for(int i=0;i<5;i++){a.Insert(i);}cout<<"数组的输出如下:"<<endl;for(int i=0;i<5;i++){cout<<a[i]<<endl;;}a.Delete();cout<<"数组的容量:"<<a.getCap()<<endl;cout<<"数组的大小:"<<a.getSize()<<endl;MyArray<int> b(a);  //拷贝构造函数调用MyArray<int> c(5);  c = a;  //operator= 的调用
}class Person
{
public:Person(){}  //自定义数据类型需提供默认构造函数的空实现Person(string name,int age){this->name = name;this->age = age;}string name;int age;
};void printPerson(MyArray<Person> &a)
{for(int i=0;i<a.getSize();i++){cout<<"姓名:"<<a[i].name<<endl;cout<<"年龄:"<<a[i].age<<endl;}
}void fun2()  //自定义数据类型测试
{MyArray<Person> a(5);Person p1("Tom",10);Person p2("Jack",12);Person p3("Danny",11);a.Insert(p1);a.Insert(p2);a.Insert(p3);printPerson(a);cout<<"数组的容量:"<<a.getCap()<<endl;cout<<"数组的大小:"<<a.getSize()<<endl;
}int main()
{fun1();fun2();system("pause");return 0;
}

程序运行后的结果如下图所示。
在这里插入图片描述


本文参考视频:
黑马程序员匠心之作|C++教程从0到1入门编程,学习编程不再难

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

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

相关文章

代码随想录算法训练营第22天—回溯算法02 | ● *216.组合总和III ● 17.电话号码的字母组合

*216.组合总和III 题目链接/文章讲解&#xff1a;https://programmercarl.com/0216.%E7%BB%84%E5%90%88%E6%80%BB%E5%92%8CIII.html 视频讲解&#xff1a;https://www.bilibili.com/video/BV1wg411873x 考点 回溯剪枝 我的思路 回溯三要素 形参&#xff1a;目标和&#xff0c…

【数据结构与算法】动态规划法解题20240227

动态规划法 一、什么是动态规划二、动态规划的解题步骤三、509. 斐波那契数1、动规五部曲&#xff1a; 四、70. 爬楼梯1、动规五部曲&#xff1a; 五、746. 使用最小花费爬楼梯1、动规五部曲&#xff1a; 一、什么是动态规划 动态规划&#xff0c;英文&#xff1a;Dynamic Pro…

qt-C++笔记之使用QProcess去执行一个可执行文件时指定动态库所存放的文件夹lib的路径

qt-C笔记之使用QProcess去执行一个可执行文件时指定动态库所存放的文件夹lib的路径 参考博文&#xff1a; 1.C笔记之执行一个可执行文件时指定动态库所存放的文件夹lib的路径 2.Linux笔记之LD_LIBRARY_PATH详解 3.qt-C笔记之使用QProcess去执行一个可执行文件时指定动态库所存放…

C# EF Core迁移数据库

现象&#xff1a; 在CodeFirst时&#xff0c;先写字段与表&#xff0c;创建数据库后&#xff0c;再添加内容 但字段与表会变更&#xff0c;比如改名删除增加等 需求&#xff1a; 当表字段变更时&#xff0c;同时变更数据库&#xff0c;执行数据库迁移 核心命令 Add-Migrat…

什么是SSH端口转发?

目录 前言&#xff1a; 一、SSH端口转发的概念 二、SSH端口转发的类型 2.1 本地端口转发 2.2 远程端口转发 2.3 动态端口转发 三、SSH端口转发的用途 3.1 安全远程访问 3.2 跨越网络限制 3.3 加密流量传输 3.4 跨越 NAT 网络 3.5 安全代理 四、总结 前言&#xff…

vue3.0 ref的使用

一.在vue2中定义变量 在使用vue2的时候,我们定义变量会在data中进行定义&#xff0c;那么我们在vue3中是如何定义变量的呢&#xff1f;我们会使用ref来进行定义。 (1)我们通过一个简单的案例来看 代码&#xff1a; <template> <div><button click"coun…

支持向量机|机器学习方法(李航)

支持向量机&#xff0c;可以看着是升级版的感知机&#xff0c;与感知机相比。他们都是找到一个超平面对数据集进行分割&#xff0c;区别在于&#xff0c;感知机模型得到的超平面空间中可以有无穷个超平面&#xff0c;但支持向量机仅含有一个&#xff0c;这一个超平面与样本点的…

matplotlib plt.show()却弹出空白框并之后自动退出程序的原因及解决方法

运行下列代码并使用plt.show()进行展示时候&#xff0c;cmd输出如下&#xff1a; 先弹出空白框&#xff1a; 而后直接退出程序&#xff1a; 之前遇到过很多次&#xff0c;由于不输出Traceback&#xff0c;完全不知道什么原因。结果发现是因为没有导入torch导致的。 解决办法就…

人工智能在测绘行业的应用与挑战

目录 一、背景 二、AI在测绘行业的应用方向 1. 自动化特征提取 2. 数据处理与分析 3. 无人机测绘 4. 智能导航与路径规划 5. 三维建模与可视化 6. 地理信息系统&#xff08;GIS&#xff09;智能化 三、发展前景 1. 技术融合 2. 精准测绘 3. 智慧城市建设 4. 可…

配置QINQ

1.实验环境&#xff1a; 某运营商接了公司A和公司B的网络&#xff0c;现需要使用qinq技术实现公司A、公司B的私有网络能够使用运营商网络互通。公司A使用灵活的qinq让内部网络的vlan 10映射为公网vlan2进行数据转发&#xff0c;vlan20映射为vlan3进行数据转发。公司B使用基本的…

「哈哥赠书活动 - 48期」-『商业分析思维与实践:用数据分析解决商业问题宣传文案』

⭐️ 赠书 - 《商业分析思维与实践》 ⭐️ 内容简介 本书以业务为导向&#xff0c;详细地讲解了如何通过大数据分析来解决商业问题。其目的在于运用大数据分析思维&#xff0c;帮助读者把学术知识应用于真实的业务场景&#xff0c;解决实际的业务问题。本书基于业务问题&#x…

Arduino单片机基础介绍

&#xff08;本文为简单介绍&#xff0c;内容源于网络和AI&#xff09; Arduino单片机&#xff0c;自2005年诞生以来&#xff0c;已经成为全球爱好者和专业工程师们快速实现创意原型的重要工具。Arduino的普及不仅因其强大的功能和简易的操作&#xff0c;还在于其背后强大的社…

Java计划线程池ScheduledThreadPoolExecutor运行流程和源码分析

1. 计划线程池ScheduledThreadPoolExecutor简介 ScheduledThreadPoolExecutor继承自线程池ThreadPoolExecutor&#xff0c;并在其基础上增加了按时间调度执行任务的功能&#xff0c;如果对ThreadPoolExecutor还不是很熟悉&#xff0c;可以阅读一下这篇文章&#xff1a; Java线…

【C++】---内存管理new和delete详解

一、C/C内存分布 C/C内存被分为6个区域&#xff1a; &#xff08;1&#xff09; 内核空间&#xff1a;存放内核代码和环境变量。 &#xff08;2&#xff09;栈区&#xff1a;向下增长&#xff08;存放非静态局部变量&#xff0c;函数参数&#xff0c;返回值等等&#xff09; …

OCPP 1.6 接入实现文档

一、简介 OCPP&#xff08;Open Charge Point Protocol&#xff09;是一个开放的通信协议&#xff0c;用于充电站&#xff08;Charge Point&#xff09;与中央系统&#xff08;Central System&#xff0c;如充电站管理系统或服务提供商平台&#xff09;之间的通讯。本篇文档将…

纽约纳斯达克大屏投放受众群体有哪些-大舍传媒

纽约纳斯达克大屏投放受众群体有哪些-大舍传媒 1. 纳斯达克大屏的概述 纳斯达克大屏是全球金融市场中最出名的电子交易平台之一。作为一个重要的金融信息传递渠道&#xff0c;纳斯达克大屏吸引了来自全球的投资者的目光。在这个巨大的投放平台上&#xff0c;大舍传媒希望为客…

【Java】线程池总结——干货满满!!!

目录 一 使用线程池的好处 二 Executor 框架 2.1 简介 2.2 Executor 框架结构(主要由三大部分组成) 1) 任务(Runnable /Callable) 2) 任务的执行(Executor) 3) 异步计算的结果(Future) 2.3 Executor 框架的使用示意图 三 (重要)ThreadPoolExecutor 类简单介绍 3.1 Thr…

如何使用Axure RP制作web页面并实现无公网ip远程访问——“cpolar内网穿透”

文章目录 前言1.在AxureRP中生成HTML文件2.配置IIS服务3.添加防火墙安全策略4.使用cpolar内网穿透实现公网访问4.1 登录cpolar web ui管理界面4.2 启动website隧道4.3 获取公网URL地址4.4. 公网远程访问内网web站点4.5 配置固定二级子域名公网访问内网web站点4.5.1创建一条固定…

Vue知识学习

Vue 是什么&#xff1f; 概念&#xff1a;Vue 是一个用于构建用户界面的渐进式框架 Vue 的两种使用方式: ① Vue 核心包开发 场景:局部 模块改造 ② Vue 核心包& Vue插件工程化开发 场景:整站开发 创建Vue 实例&#xff0c;初始化渲染的核心步骤: 1.准备容器 2.引包(官…

基于java在线调查表单系统

基于java在线调查表单系统 一、演示效果二、特性汇总三、下载链接 一、演示效果 二、特性汇总 多种技术方案&#xff0c;满足不同的技术选型需求完善的浏览器兼容、保证传统客户也能正常使用部署简单&#xff0c;一行命令完成部署更新方便&#xff0c;直接替换原安装文件不用担…