Template_C++

C++模板

C++提供了function template.

function template:实际上是建立一个通用函数,其函数类型和形参类型不具体制定,用一个虚拟的类型来代表。这个通用的函数就称为函数模版。

是不是可以这样理解,函数模版就是给了一种功能,能够适合多种数据的功能?

  • C++提供两种模版机制:函数模版和类模版。
  • 类属-类型参数化,又称参数模版。

总结:

  • 模版把函数或类要处理的数据类型参数化,表现为参数的多态性,称为类属。
  • 模版用于表达逻辑结构相同,但具体数据元素类型不同的数据对象的通用行为
1 函数模板

用函数模版,是为了实现泛型,减轻编程的工作量,增强函数的重要性

#include <iostream>template<typename T>
void Myswap(T& a, T& b){T temp = a;a = b;b = temp;
}int main(){int a = 10, b = 20;Myswap(a, b);std::cout << "a = " << a << " b = " << b << std::endl;double x = 1.5, y = 2.5;Myswap(x, y);std::cout << "x = " << x << " y = " << y << std::endl;char c = 'a', d = 'b';Myswap(c, d);std::cout << "c = " << c << " d = " << d << std::endl;return EXIT_SUCCESS;
}

在上述的代码中,我们写了一个基于模板的交换函数,可以用于交换int类型、double类型和char类型,如果不使用模板函数,就需要写三个交换函数。

void Myswap(T& a, T& b);
void Myswap(int& a   , int& b);
void Myswap(double& a, double& b);
void Myswap(char& a  , char& b);
1.1 函数模板和普通函数区别
  • 函数模板不允许自动类型转化,必须严格匹配类型;
  • 普通函数能够自动进行类型转化
#include <iostream>template<typename T>
T MyPlus(T a, T b){std::cout << "int TemplateT + T = " << a + b << std::endl;T ret = a + b;return ret;
}int MyPlus(int a, char b){std::cout << "int noTemplate char + int = " << a + b << std::endl;int ret = a + b;return ret;
}int main(){std::cout << "Hello World!" << std::endl;int a = 10;char b = 'a';MyPlus(a,a); // function template MyPlus<int>(a, a) is calledMyPlus(b,b); // function template MyPlus<char>(a, b) is calledMyPlus(a,b); // function MyPlus(int, char) is called// 普通函数可以隐式类型转换MyPlus(b,a); //function template MyPlus<char>(b, a) is calledreturn EXIT_SUCCESS;
}/*
Hello World!
int TemplateT + T = 20
int TemplateT + T = 194
int noTemplate char + int = 107
int noTemplate char + int = 107
*/
/* why not define template 
template<typename T1, typename T2>
T1 MyPlus(T1 a, T2 b) {std::cout << "T1,T2 Template T2 + T1 = " << a + b << std::endl;T1 ret = a + b;return ret;
}
MyPlus(a,b); // template<typename T1, typename T2> is called
MyPlus(b,a); // template<typename T1, typename T2> is called
*/
1.1.1 为什么函数模板不允许自动类型转换

在C++中,函数模板不允许自动类型转换的主要原因是为了保持类型安全和编译时的类型检查。以下是几个关键原因:

  1. 类型安全:函数模板的设计初衷是提供一种泛型编程的方式,使得代码可以处理多种数据类型。为了确保类型安全,编译器需要确保模板参数的类型是精确匹配的。如果允许自动类型转换,可能会导致意外的类型转换和潜在的类型错误。
  2. 编译时类型检查:C++是一种静态类型语言,编译器在编译时会进行严格的类型检查。函数模板参数的精确匹配确保了编译器可以在编译时发现类型不匹配的错误,而不是在运行时才发现问题。
  3. 避免二义性和不确定性:如果允许自动类型转换,可能会导致函数调用的二义性。例如,如果有多个函数模板可以匹配某个调用,编译器将难以确定应该选择哪个模板实例。通过要求精确匹配,可以避免这种二义性。
  4. 模板特化和重载:C++允许对模板进行特化和重载,这提供了更灵活的类型处理方式。如果允许自动类型转换,可能会干扰这些机制的正常工作,使得模板特化和重载的规则变得复杂和难以管理。

例如,考虑以下函数模板:

template <typename T>
T add(T a, T b) {return a + b;
}

如果允许自动类型转换,调用 add(5, 3.2) 可能会导致编译器尝试将 int 转换为 double 或反之,这不仅会增加编译器的复杂性,还可能导致意外的结果。

因此,为了保持类型安全、编译时类型检查和避免二义性,C++中的函数模板不允许自动类型转换。如果需要处理不同类型的参数,可以使用多个类型参数的模板、模板特化或重载函数来实现

1.1.2 自动类型转换

在C++中,自动类型转换(也称为隐式类型转换)是指编译器在需要时自动将一种数据类型转换为另一种数据类型的过程。这种转换通常发生在不同数据类型的变量进行运算或赋值时,以确保操作能够顺利进行。

C++中的自动类型转换遵循一定的规则和优先级,常见的自动类型转换包括:

  1. 整数提升:较小的整数类型(如 charshort)在表达式中会被自动提升为 int 类型。
  2. 整数和浮点数之间的转换:整数类型可以自动转换为浮点数类型,但可能会导致精度损失。
  3. 派生类到基类的转换:派生类对象可以自动转换为基类对象。
  4. 算术转换:在算术运算中,操作数会被转换为相同的类型,通常是表达式中最高级别的类型。

以下是一些C++中自动类型转换的示例:

#include <iostream>int main() {int a = 5;       // 整数类型double b = 3.2;  // 浮点数类型double c = a + b;  // 自动将整数 a 转换为浮点数类型,然后进行加法运算std::cout << c << std::endl;  // 输出 8.2char ch = 'A';  // 字符类型int i = ch;     // 自动将字符类型转换为整数类型std::cout << i << std::endl;  // 输出 65('A' 的 ASCII 值)return 0;
}

在这个示例中,整数 a 被自动转换为浮点数类型,以便与浮点数 b 进行加法运算。字符 ch 被自动转换为整数类型,以便赋值给整数变量 i

1.2 函数模板和普通函数在一起调用规则

在C++中,函数模板和普通函数可以共存,并且在调用时遵循一定的规则来确定调用哪个函数。这些规则包括函数模板实例化、普通函数的重载解析以及模板参数推导等。以下是一些关键规则:

  1. 普通函数的优先级:如果存在一个普通函数,其参数类型与调用参数完全匹配,那么编译器会优先选择这个普通函数,而不是实例化一个函数模板。
  2. 模板参数推导:如果调用参数与普通函数不匹配,编译器会尝试推导函数模板的参数类型,并实例化一个模板函数。
  3. 重载解析:如果存在多个函数模板或普通函数可以匹配调用参数,编译器会进行重载解析,选择最匹配的函数。

以下是一个示例,展示了函数模板和普通函数在一起调用的规则:

#include <iostream>// 普通函数
int add(int a, int b) {return a + b;
}// 函数模板
template <typename T>
T add(T a, T b) {return a + b;
}int main() {int a = 5,b = 3;double c = 3.2, d = 2.1;// 调用普通函数,因为参数类型完全匹配std::cout << add(a, b) << std::endl;  // 输出 8// 调用函数模板,因为普通函数不匹配 double 类型std::cout << add(c, d) << std::endl;  // 输出 5.3// 调用函数模板,因为普通函数不匹配 double 和 int 的组合std::cout << add(a, c) << std::endl;  // 输出 8.2(注意:这里会进行隐式转换)return 0;
}

在这个示例中:

  • add(a, b) 调用普通函数 int add(int, int),因为参数类型完全匹配。
  • add(c, d) 调用函数模板 T add(T, T),因为普通函数不匹配 double 类型。
  • add(a, c) 调用函数模板 T add(T, T),因为普通函数不匹配 intdouble 的组合。在这种情况下,编译器会尝试推导模板参数类型,但由于模板参数必须一致,a 会被隐式转换为 double 类型。

总结来说,函数模板和普通函数在一起调用时,编译器会根据参数类型匹配、模板参数推导和重载解析等规则来确定调用哪个函数。普通函数如果参数类型完全匹配,会优先被调用;否则,编译器会尝试实例化函数模板。

1.3 模板的编译过程

?为什么函数模板可以和普通函数放在一起?

编译过程:预处理(Pre-processing)->编译(Compiling)-> 汇编(Assembling)-> 链接(Linking)

image-20240716105436980

函数模板机制理论

  • 编译器并不是把函数模板处理成能够处理成任何类型的函数
  • 函数模板通过具体类型产生不同的函数
  • 编译器对函数模板进行两次编译,在声明的地方对模板代码本身进行编译,在调用的地方对参数替换后的代码进行编译。

局限性:函数模板很有可能无法处理某些类型,比如两数相加的模板就无法处理两个数组相加。此时可以使用模板重载的方式解决。

1.4 函数模板重载

在C++中,函数模板可以像普通函数一样进行重载。函数模板重载允许定义多个具有相同名称但参数类型或数量不同的模板函数。编译器会根据调用时的参数类型和数量来选择合适的模板实例。

以下是一些函数模板重载的示例:

#include <iostream>// 函数模板 1:接受两个相同类型的参数
template <typename T>
T add(T a, T b) {return a + b;
}
// 函数模板 2:接受三个相同类型的参数
template <typename T>
T add(T a, T b, T c) {return a + b + c;
}
// 普通函数:接受两个不同类型的参数
double add(int a, double b) {return a + b;
}
int main() {int a = 5,b = 3,c = 2;double d = 3.2;// 调用函数模板 1std::cout << add(a, b) << std::endl;  // 输出 8// 调用函数模板 2std::cout << add(a, b, c) << std::endl;  // 输出 10// 调用普通函数std::cout << add(a, d) << std::endl;  // 输出 8.2return 0;
}

在这个示例中:

  1. add(a, b) 调用第一个函数模板 T add(T, T),因为参数类型相同。
  2. add(a, b, c) 调用第二个函数模板 T add(T, T, T),因为参数类型相同且数量为三个。
  3. add(a, d) 调用普通函数 double add(int, double),因为参数类型不同且数量为两个。

函数模板重载的规则与普通函数重载类似,编译器会根据参数类型和数量进行匹配,选择最合适的函数模板实例或普通函数。如果存在多个匹配的模板实例,编译器会进行重载解析,选择最匹配的实例。

需要注意的是,函数模板的重载并不意味着编译器会自动进行类型转换。模板参数必须精确匹配,否则编译器会报错。如果需要处理不同类型的参数,可以使用多个类型参数的模板或模板特化来实现。

1.5 函数模板特化

在C++中,template<> 语法用于模板特化。模板特化允许为特定类型提供专门的实现。你提到的 template<> void mySwap<persion>(persion &p1, persion &p2) 是一个函数模板的显式特化,用于处理 persion 类型的交换操作。

假设你有一个通用的 mySwap 函数模板,用于交换两个对象:

template <typename T>
void mySwap(T &a, T &b) {T temp = a;a = b;b = temp;
}

如果你希望为 persion 类型提供一个专门的实现,可以使用模板特化:

#include <iostream>
#include <string>// 定义 persion 类
class persion {
public:persion(const std::string& name, int age) : name(name), age(age) {}void print() const {std::cout << "Name: " << name << ", Age: " << age << std::endl;}std::string name;int age;// 友元声明,允许 mySwap 函数访问私有成员void mySwap(persion &p1, persion &p2);
};// 通用的 mySwap 函数模板
template <typename T>
void mySwap(T &a, T &b) {T temp = a;a = b;b = temp;
}// 针对 persion 类型的 mySwap 函数模板特化
template<> void mySwap<persion>(persion &p1, persion &p2) {using std::swap;swap(p1.name, p2.name);swap(p1.age, p2.age);
}int main() {persion p1("Alice", 30);persion p2("Bob", 25);std::cout << "Before swap:" << std::endl;p1.print(); p2.print();mySwap(p1, p2);std::cout << "After swap:" << std::endl;p1.print(); p2.print();return 0;
}
/*
Before swap:
Name: Alice, Age: 30
Name: Bob, Age: 25
After swap:
Name: Bob, Age: 25
Name: Alice, Age: 30
*/

在这个示例中:

  1. 定义了一个通用的 mySwap 函数模板,用于交换两个对象。
  2. 定义了一个 persion 类,包含 nameage 成员变量。
  3. 使用 template<> 语法为 persion 类型提供了一个专门的 mySwap 实现,交换 persion 对象的 nameage 成员变量。

通过这种方式,你可以为特定类型提供定制的交换操作,而不影响其他类型的交换行为。

类模板

类模板和函数模板的定义和使用类似。

类模板用于实现类所需数据的类型参数化

在C++中,类模板(class template)是一种强大的工具,用于实现类所需数据的类型参数化。类模板允许你定义一个通用的类,其中的数据类型可以作为参数传递,从而使得类可以处理多种不同类型的数据。

以下是一个简单的类模板示例,展示了如何使用类模板实现类型参数化:

#include <iostream>
#include <vector>// 定义一个类模板,用于存储和管理数据
template <typename T>
class Container {
public:// 添加元素到容器中void add(const T& item) {data.push_back(item);}// 打印容器中的所有元素void print() const {for (const T& item : data) {std::cout << item << " ";}std::cout << std::endl;}private:std::vector<T> data; // 使用 vector 存储数据
};int main() {// 创建一个存储 int 类型数据的 Container 实例Container<int> intContainer;intContainer.add(1);intContainer.add(2);intContainer.add(3);intContainer.print(); // 输出: 1 2 3// 创建一个存储 double 类型数据的 Container 实例Container<double> doubleContainer;doubleContainer.add(1.1);doubleContainer.add(2.2);doubleContainer.add(3.3);doubleContainer.print(); // 输出: 1.1 2.2 3.3// 创建一个存储 std::string 类型数据的 Container 实例Container<std::string> stringContainer;stringContainer.add("Hello");stringContainer.add("World");stringContainer.print(); // 输出: Hello Worldreturn 0;
}
/*
1 2 3 
1.1 2.2 3.3
Hello World
*/

在这个示例中:

  1. 定义了一个名为 Container 的类模板,其中的 T 是一个类型参数。
  2. Container 类包含两个成员函数:add 用于添加元素到容器中,print 用于打印容器中的所有元素。
  3. 使用 std::vector<T> 来存储数据,这样可以方便地管理动态数组。
  4. main 函数中,创建了三个不同类型的 Container 实例:一个用于存储 int 类型数据,一个用于存储 double 类型数据,一个用于存储 std::string 类型数据。

通过这种方式,类模板允许你编写一次代码,然后使用不同的类型参数来实例化类,从而实现类型参数化。这大大提高了代码的复用性和灵活性。

类模板做函数参数

在C++中,类模板可以作为函数参数,这使得函数能够处理不同类型的类模板实例。你可以通过模板参数来传递类模板实例,或者直接在函数参数中使用类模板。

以下是一些示例,展示了如何将类模板作为函数参数:

示例 1:通过模板参数传递类模板实例

#include <iostream>
#include <vector>// 定义一个类模板
template <typename T>
class Container {
public:void add(const T& item) {data.push_back(item);}void print() const {for (const T& item : data) {std::cout << item << " ";}std::cout << std::endl;}
private:std::vector<T> data;
};// 定义一个函数模板,接受一个 Container 实例
template <typename T>
void processContainer(const Container<T>& container) {container.print();
}
int main() {Container<int> intContainer;intContainer.add(1); intContainer.add(2); intContainer.add(3);processContainer(intContainer); // 输出: 1 2 3Container<double> doubleContainer; doubleContainer.add(1.1); doubleContainer.add(2.2); doubleContainer.add(3.3);processContainer(doubleContainer); // 输出: 1.1 2.2 3.3return 0;
}

示例 2:直接在函数参数中使用类模板

// 定义一个函数,接受一个 Container 实例
void processIntContainer(const Container<int>& container) {container.print();
}
void processDoubleContainer(const Container<double>& container) {container.print();
}

在这两个示例中,我们展示了如何将类模板实例作为函数参数传递。第一个示例通过模板参数传递类模板实例,使得函数可以处理不同类型的 Container 实例。第二个示例直接在函数参数中使用类模板,为每种类型定义了一个单独的函数。

通过这种方式,你可以编写灵活的函数,处理不同类型的类模板实例

类模板派生普通类

在C++中,可以从类模板派生出普通类。这种情况下,普通类会固定使用类模板中的某个特定类型。以下是一个示例,展示了如何从类模板派生出普通类:

#include <iostream>
#include <vector>// 定义一个类模板
template <typename T>
class BaseContainer {
public:void add(const T& item) {data.push_back(item);}void print() const {for (const T& item : data) {std::cout << item << " ";}std::cout << std::endl;}
protected:std::vector<T> data;
};// 从类模板派生出一个普通类
class IntContainer : public BaseContainer<int> {
public:void addMultiple(int count, int value) {for (int i = 0; i < count; ++i) {add(value);}}
};int main() {IntContainer intContainer;intContainer.add(1); ntContainer.add(2); ntContainer.add(3);intContainer.print(); // 输出: 1 2 3intContainer.addMultiple(3, 4);intContainer.print(); // 输出: 1 2 3 4 4 4return 0;
}

在这个示例中:

  1. 定义了一个名为 BaseContainer 的类模板,其中的 T 是一个类型参数。
  2. BaseContainer 类包含两个成员函数:add 用于添加元素到容器中,print 用于打印容器中的所有元素。
  3. BaseContainer 类模板派生出一个普通类 IntContainer,并固定使用 int 类型。
  4. IntContainer 类添加了一个新的成员函数 addMultiple,用于添加多个相同值的元素。

通过这种方式,你可以从类模板派生出普通类,并固定使用某个特定类型。这使得代码更加简洁和易于维护。

类模板派生类模板

类模板派生类模板是指从一个类模板派生出另一个类模板。这种技术在C++中非常有用,可以实现代码的重用和泛化。下面是一个简单的示例,展示了如何从一个类模板派生出另一个类模板。

#include <iostream>// 基类模板
template <typename T>
class Base {
public:Base(T value) : data(value) {}void display() const {std::cout << "Base: " << data << std::endl;}
protected:T data;
};// 派生类模板
template <typename T>
class Derived : public Base<T> {
public:Derived(T value) : Base<T>(value) {}void display() const {std::cout << "Derived: " << this->data << std::endl;}
};int main() {Base<int> base(10);base.display();Derived<int> derived(20);derived.display();return 0;
}

在这个示例中,我们定义了一个基类模板 Base,它包含一个类型为 T 的数据成员 data 和一个显示数据的成员函数 display。然后,我们定义了一个派生类模板 Derived,它继承自 Base 模板,并重写了 display 函数。

main 函数中,我们创建了一个 Base<int> 对象和一个 Derived<int> 对象,并分别调用它们的 display 函数来显示数据。

通过这种方式,我们可以灵活地使用类模板派生类模板,实现更复杂和通用的代码结构。

类模板类内实现

在C++中,类模板的成员函数可以在类内实现,也可以在类外实现。类内实现通常用于简单的成员函数,这样可以使得代码更加紧凑和易读。下面是一个示例,展示了如何在类模板中实现成员函数。

#include <iostream>template <typename T>
class MyClass {
public:// 构造函数MyClass(T value) : data(value) {}// 成员函数在类内实现void display() const {std::cout << "Data: " << data << std::endl;}private:T data;
};int main() {MyClass<int> obj(10);obj.display();MyClass<double> obj2(3.14);obj2.display();return 0;
}

在这个示例中,我们定义了一个类模板 MyClass,它包含一个类型为 T 的数据成员 data 和一个显示数据的成员函数 displaydisplay 函数在类内实现,直接在类定义中给出其实现。

main 函数中,我们创建了一个 MyClass<int> 对象和一个 MyClass<double> 对象,并分别调用它们的 display 函数来显示数据。

类内实现的优势在于代码更加紧凑,适合简单的成员函数。如果成员函数较为复杂,或者需要在多个地方使用相同的实现,可以考虑在类外实现成员函数。

类模板类外实现

在C++中,类模板的成员函数也可以在类外实现。这种做法通常用于成员函数较为复杂,或者需要在多个地方使用相同的实现。下面是一个示例,展示了如何在类模板中实现成员函数。

#include <iostream>template <typename T>
class MyClass {
public:// 构造函数MyClass(T value);// 成员函数声明void display() const;private:T data;
};// 构造函数在类外实现
template <typename T>
MyClass<T>::MyClass(T value) : data(value) {}// 成员函数在类外实现
template <typename T>
void MyClass<T>::display() const {std::cout << "Data: " << data << std::endl;
}int main() {MyClass<int> obj(10);obj.display();MyClass<double> obj2(3.14);obj2.display();return 0;
}

在这个示例中,我们定义了一个类模板 MyClass,它包含一个类型为 T 的数据成员 data 和一个显示数据的成员函数 displaydisplay 函数在类外实现,需要在类定义之外给出其实现。

main 函数中,我们创建了一个 MyClass<int> 对象和一个 MyClass<double> 对象,并分别调用它们的 display 函数来显示数据。

类外实现的优势在于可以将复杂的成员函数实现分离出来,使得类定义更加简洁。同时,相同的实现可以在多个地方使用,提高了代码的重用性。

类模板头文件和源文件分离问题

==注意:==类模板的声明和实现放到一个文件中,我们把这个文件命名为.hpp(这个是约定的规则,并不是标准,必须这么写)。

原因:

  • 类模板需要二次编译,在出现模板的地方编译一次,在调用模板的地方再次编译。
  • C++编译规则为独立编译。
模板类与友元函数

友元函数是可以访问类的私有变量(private)和保护变量(protected)。

在C++中,模板类可以声明友元函数,以便友元函数能够访问类的私有成员。友元函数可以是普通函数、类成员函数,甚至是另一个模板类的成员函数。下面是一个示例,展示了如何在模板类中声明友元函数。

#include <iostream>// 模板类
template <typename T>
class MyClass {
public:MyClass(T value) : data(value) {}// 声明友元函数friend void display(const MyClass<T>& obj);private:T data;
};// 友元函数的实现
template <typename T>
void display(const MyClass<T>& obj) {std::cout << "Data: " << obj.data << std::endl;
}int main() {MyClass<int> obj(10);display(obj);MyClass<double> obj2(3.14);display(obj2);return 0;
}

在这个示例中,我们定义了一个模板类 MyClass,它包含一个类型为 T 的数据成员 data。我们在类中声明了一个友元函数 display,该函数可以访问 MyClass 的私有成员 data

友元函数 display 的实现位于类外,并且需要使用模板参数 T 来匹配类模板。在 main 函数中,我们创建了一个 MyClass<int> 对象和一个 MyClass<double> 对象,并分别调用友元函数 display 来显示数据。

通过这种方式,模板类可以与友元函数协同工作,实现对私有成员的访问和操作。

template template parameters

在C++中,模板模板参数(template template parameters)允许你在定义模板时使用另一个模板作为参数。这种技术在需要参数化容器类型时特别有用。下面是一个示例,展示了如何使用模板模板参数。

#include <iostream>
#include <vector>
#include <deque>template <template <typename...> class Container, typename T>
class MyContainer {
public:MyContainer() {std::cout << "Constructor" << std::endl;}void display() {// 假设容器有一个 size() 方法和一个 operator[] 方法std::cout << "Size: " << container.size() << std::endl;for (size_t i = 0; i < container.size(); ++i) {std::cout << container[i] << " ";}std::cout << std::endl;}void push_back1(const T& value) {container.push_back(value);std::cout << "Push back " << value << std::endl;std::cout << "Size: " << container.size() << std::endl;std::cout << "Capacity: " << container.capacity() << std::endl;std::cout << "Front: " << container.front() << std::endl;}void dequeue1() {if (!container.empty()) {container.pop_front();}}
private:Container<T> container;
};int main() {// 使用 vector 作为容器类型MyContainer<std::vector, int> vecContainer;vecContainer.push_back1(1); vecContainer.push_back1(2);vecContainer.push_back1(3);vecContainer.display();// 使用 deque 作为容器类型MyContainer<std::deque, int> dequeContainer;dequeContainer.push_back1(1);dequeContainer.push_back1(2);dequeContainer.push_back1(3);dequeContainer.display();return 0;
}

在这个示例中,我们定义了一个模板类 MyContainer,它接受两个模板参数:一个模板模板参数 Container 和一个类型参数 TContainer 是一个模板类,而 T 是容器中元素的类型。

MyContainer 的构造函数中,我们使用模板模板参数 Container 创建了一个容器,并向其中添加了一个默认构造的 T 类型的元素。

main 函数中,我们创建了两个 MyContainer 对象,一个使用 std::vector 作为容器类型,另一个使用 std::deque 作为容器类型,并分别调用它们的 display 函数来显示容器中的元素。

通过这种方式,模板模板参数允许我们在定义模板时参数化容器类型,从而实现更灵活和通用的代码。

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

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

相关文章

Keil开发IDE

Keil开发IDE 简述Keil C51Keil ARMMDK DFP安装 简述 Keil公司是一家业界领先的微控制器&#xff08;MCU&#xff09;软件开发工具的独立供应商。Keil公司由两家私人公司联合运营&#xff0c;分别是德国慕尼黑的Keil Elektronik GmbH和美国德克萨斯的Keil Software Inc。Keil公…

WebRTC音视频-环境搭建

目录 期望效果 1:虚拟机和系统安装 2:WebRTC客户端环境搭建 2.1&#xff1a;VScode安装 2.2&#xff1a;MobaXterm安装 3:WebRTC服务器环境搭建 3.1&#xff1a;安装openssh服务器 3.2&#xff1a;安装Node.js 3.3&#xff1a;coturn穿透和转发服务器 3.3.1&a…

全国数据智能与智慧政务行业产教融合共同体学术年会暨广东行政职业学院(广东青年职业学院)第一届“求是论坛”成功举办

为进一步深化现代职业教育体系建设理论研究&#xff0c;丰富行业产教融合共同体实践探索&#xff0c;7月13日&#xff0c;全国数据智能与智慧政务行业产教融合共同体学术年会暨广东行政职业学院&#xff08;广东青年职业学院&#xff09;第一届“求是论坛”在广东行政职业学院&…

【iOS】static、extern、const、auto关键字以及联合使用

目录 前言extern关键字static关键字const关键字 联合使用static和externstatic和constextern和const auto关键字 先了解一下静态变量所在的全局/静态区的特点&#xff1a;【iOS】内存五大分区 前言 上面提到的全局/静态区中存放的是全局变量或静态变量&#xff1a; 全局变量…

LabVIEW软件开发的雷区在哪里?

在LabVIEW软件开发中&#xff0c;有几个需要注意的雷区&#xff0c;以避免常见的错误和提高开发效率&#xff1a; 1. 不良的代码结构 雷区&#xff1a;混乱的代码结构和不清晰的程序逻辑。 后果&#xff1a;导致难以维护和调试的代码&#xff0c;增加了错误和故障的风险。 …

AI算法18-最小角回归算法Least Angle Regression | LARS

​​​ 最小角回归算法简介 最小角回归&#xff08;Least Angle Regression, LAR&#xff09;是一种用于回归分析的统计方法&#xff0c;它在某些方面类似于最小二乘回归&#xff0c;但提供了一些额外的优点。最小角回归由Bradley Efron等人提出&#xff0c;主要用于处理具有…

Leetcode双指针法应用

1.双指针法 文章目录 1.双指针法1.1什么是双指针法&#xff1f;1.2解题思路1.3扩展 1.1什么是双指针法&#xff1f; 双指针算法是一种在数组或序列上操作的技巧&#xff0c;实际上是对暴力枚举算法的一种优化&#xff0c;通常涉及到两个索引&#xff08;或指针&#xff09;从两…

【D3.js in Action 3 精译_020】2.6 用 D3 设置与修改元素样式 + 名人专访(Nadieh Bremer)+ 2.7 本章小结

当前内容所在位置 第一部分 D3.js 基础知识 第一章 D3.js 简介&#xff08;已完结&#xff09; 1.1 何为 D3.js&#xff1f;1.2 D3 生态系统——入门须知1.3 数据可视化最佳实践&#xff08;上&#xff09;1.3 数据可视化最佳实践&#xff08;下&#xff09;1.4 本章小结 第二章…

Chromium CI/CD 之Jenkins实用指南2024-在Windows节点上创建任务(九)

1. 引言 在现代软件开发流程中&#xff0c;持续集成&#xff08;CI&#xff09;和持续交付&#xff08;CD&#xff09;已成为确保代码质量和加速发布周期的关键实践。Jenkins作为一款广泛应用的开源自动化服务器&#xff0c;通过其强大的插件生态系统和灵活的配置选项&#xf…

Spring Boot项目中使用MyBatis Generator (MBG) 自动生成Mapper文件

Spring Boot项目中使用MyBatis Generator (MBG) 自动生成Mapper文件可以很大程度上减少编码。本文着重介绍如何在实战中使用MGB自动生成Mapper文件 1. 添加MyBatis Generator依赖 在pom.xml中添加必要的依赖 <dependency><groupId>org.mybatis.spring.boot</…

如何在Linux上部署Ruby on Rails应用程序

在Linux上部署Ruby on Rails应用程序是一个相对复杂的过程&#xff0c;需要按照一系列步骤进行。下面是一个基本的部署过程&#xff0c;涵盖了从安装所需软件到部署应用程序的所有步骤。 安装必要的软件 在部署Ruby on Rails应用程序之前&#xff0c;需要确保Linux系统上安装了…

【LeetCode】day15:110 - 平衡二叉树, 257 - 二叉树的所有路径, 404 - 左叶子之和, 222 - 完全二叉树的节点个数

LeetCode 代码随想录跟练 Day15 110.平衡二叉树257.二叉树的所有路径404.左叶子之和222.完全二叉树的节点个数 110.平衡二叉树 题目描述&#xff1a; 给定一个二叉树&#xff0c;判断它是否是 平衡二叉树 平衡二叉树的定义是&#xff0c;对于树中的每个节点&#xff0c;其左右…

三、初识C语言(3)

1.操作符 &#xff08;1&#xff09;算术操作符 - * / % 商 余&#xff08;取模&#xff09; 小算法&#xff1a; 若a<b&#xff0c;则a%b a 若a%b c&#xff0c;则0 < c < b-1 若两个int 类型数相除&#xff0c;结果有小数会被舍弃。 保留小数…

阿里云 申请免费ssl 证书

1控制台--数字证书管理服务 2 创建所需域名证书

下载安装VSCode并添加插件作为仓颉编程入门编辑器

VSCode下载地址&#xff1a;下载 Visual Studio Code - Mac、Linux、Windows 插件下载&#xff1a;GitCode - 全球开发者的开源社区,开源代码托管平台 仓颉社区中下载解压 cangjie.vsix 插件 打开VSCode 按 Ctrl Shift X 弹出下图 按照上图步骤依次点击选中我们下…

openstack设置IP直接登录,不需要加dashboard后缀

openstack 实验环境&#xff0c;openstack-t版&#xff0c;centos2009 修改配置文件 [rootcontroller ~]# vim /WEBROOT /etc/openstack-dashboard/local_settings #将dashboard去掉 WEBROOT /dashboard/ #改为 WEBROOT /[rootcontroller ~]# vim /etc/httpd/conf.d/openst…

vscode搭建PyQt + Quick开发环境

VScode搭建PyQt Quick开发环境 目录 环境准备 &#x1f514;安装必要的Python包 &#x1f514;&#x1f50e; PyQt5和PySide2的区别&#x1f4be; 安装PyQt5&#x1f4be; 安装PySide2 配置VScode &#x1f514;&#x1f4bb; 安装扩展 代码示例 &#x1f514;✔ Python调用Qt…

分布式 I/O 系统Modbus TCP 耦合器BL200

BL200 耦合器是一个数据采集和控制系统&#xff0c;基于强大的 32 位微处理器设计&#xff0c;采用 Linux 操作系统&#xff0c;可以快速接入现场 PLC、SCADA 以及 ERP 系统&#xff0c; 内置逻辑控制、边缘计算应用&#xff0c;支持标准 Modbus TCP 服务器通讯&#xff0c;以太…

光耦合器技术的实际应用

光耦合器也称为光隔离器&#xff0c;是现代电子产品中的关键组件&#xff0c;可确保电路不同部分之间的信号完整性和隔离。它们使用光来传输电信号&#xff0c;提供电气隔离和抗噪性。 结构和功能 光耦合器通常由以下部分组成&#xff1a; 1.LED&#xff08;发光二极管&#…

pytorch学习(七)torchvision.datasets的使用

网络上已经有公开的数据集&#xff0c;并且这些数据集被整合到了torchvision.datasets中&#xff0c;使用自带的函数可以直接下载。 1.数据集 具体有哪些数据可直接用torchvision.datasets加载呢&#xff1f;可以查看这个网址&#xff1a; datasets官网&#xff1a;Datasets…