跳到主要内容
极客日志极客日志面向AI+效率的开发者社区
首页博客我的书AI学习GitHub 精选镜像AI工具UI配色美学关于
搜索内容 / 工具 / 仓库 / 镜像...⌘K搜索
注册
博客列表
C++算法

C++ 模板:泛型编程与代码复用

C++ 模板是泛型编程的核心工具,分为函数模板和类模板。通过模板参数实现类型无关的通用代码,编译器在实例化阶段生成具体类型代码。支持隐式和显式实例化,非类型模板参数允许编译期常量定制。模板特化(全特化、偏特化)用于处理特殊类型逻辑。工程实践中需注意分离编译问题,通常将声明与定义放在同一头文件或使用显式实例化解决。

kaikai发布于 2026/3/24更新于 2026/10/25110 浏览
C++ 模板:泛型编程与代码复用

一、泛型编程

1.1 为什么需要泛型编程?

先看一个经典的例子:实现交换两个变量的函数。如果不使用模板,我们需要为每种类型编写独立的重载函数:

// 交换 int 类型
void Swap(int& left, int& right) { 
    int temp = left; 
    left = right; 
    right = temp; 
}
// 交换 double 类型
void Swap(double& left, double& right) { 
    double temp = left; 
    left = right; 
    right = temp; 
}
// 交换 char 类型
void Swap(char& left, char& right) { 
    char temp = left; 
    left = right; 
    right = temp; 
}

这种方式的弊端很明显:

代码复用率低:所有重载函数的逻辑完全一致,只是变量类型不同,存在大量冗余代码;

可维护性差:如果需要修改交换逻辑(比如增加日志输出),必须修改所有重载函数,容易出错;

扩展性不足:新增类型(如 long、std::string)时,需要手动添加对应的重载函数,无法自动适配。

而泛型编程的核心思想,就是编写与类型无关的通用代码,将重复的类型相关工作交给编译器处理。就像用模具浇筑零件一样,模板就是那个'通用模具',我们只需传入不同的'材料'(类型),编译器就会自动生成对应类型的'零件'(具体代码)。

1.2 模板:泛型编程的基础

模板是泛型编程的核心工具,它分为函数模板和类模板两类:

**函数模板:**针对函数的通用实现,用于生成不同类型的函数;

**类模板:**针对类的通用实现,用于生成不同类型的类(如 STL 中的 vector、list 等容器)。

模板的本质是'代码生成器'——它本身并不是可执行代码,而是编译器用来生成具体类型代码的蓝图。


二、函数模板

2.1 函数模板的定义格式

函数模板的定义需要使用 template 关键字声明模板参数,语法格式如下:

template<typename T1, typename T2, ..., typename Tn>
返回值类型 函数名 (参数列表) {
    // 函数体(与类型无关的通用逻辑)
}

**template:**声明模板的关键字,必须放在函数定义之前;

**typename:**定义模板参数的关键字,也可以用 class 替代(注意:不能用 struct 代替 class);

**T1、T2...Tn:**模板参数(类型占位符),表示'待确定的类型',在函数使用时会被具体类型替换。

以交换函数为例,用函数模板改写后,代码会极度简洁:

// 函数模板:通用交换函数
template<typename T>
void Swap(T& left, T& right) {
    T temp = left;
    left = right;
    right = temp;
}

这里的 T 就是模板参数,编译器会根据传入的实参类型,自动将 T 替换为 int、double、char 等具体类型,生成对应的交换函数。

2.2 函数模板的原理

很多人会误以为函数模板是'一个能处理所有类型的函数',但实际上并非如此。函数模板本身不是函数,而是编译器生成具体函数的'模具'。

在编译器编译阶段,当我们调用函数模板时,编译器会根据传入的实参类型,推演模板参数 T 的具体类型,然后生成一份专门处理该类型的函数代码。这个过程称为'模板实例化'。

举个例子:

int main() {
    int a = 10, b = 20;
    Swap(a, b); // 传入 int 类型实参
    double c = 3.14, d = 6.28;
    Swap(c, d); // 传入 double 类型实参
    char e = 'A', f = 'B';
    Swap(e, f); // 传入 char 类型实参
    return 0;
}

编译器编译时,会分别生成 3 个具体的交换函数:

// 编译器为 int 类型生成的函数
void Swap(int& left, int& right) {
    int temp = left;
    left = right;
    right = temp;
}
// 编译器为 double 类型生成的函数
void Swap(double& left, double& right) {
    double temp = left;
    left = right;
    right = temp;
}
// 编译器为 char 类型生成的函数
void Swap(char& left, char& right) {
    char temp = left;
    left = right;
    right = temp;
}

简单来说,函数模板的原理就是'编译器帮我们做重复的工作',将手动编写重载函数的过程自动化,既减少了冗余代码,又降低了维护成本。

2.3 函数模板的实例化

函数模板的实例化,就是编译器根据具体类型生成对应函数的过程。根据是否显式指定模板参数,分为隐式实例化和显式实例化两种。

2.3.1 隐式实例化

隐式实例化是指:编译器根据传入的实参类型,自动推演模板参数 T 的具体类型,无需手动指定。

template<typename T>
T Add(const T& left, const T& right) {
    return left + right;
}
int main() {
    int a1 = 10, a2 = 20;
    Add(a1, a2); // 隐式实例化:T 被推演为 int
    double d1 = 10.0, d2 = 20.0;
    Add(d1, d2); // 隐式实例化:T 被推演为 double
    return 0;
}

注意:编译器在隐式实例化时,不会进行自动类型转换。如果传入的实参类型不一致,会直接编译报错:

int a = 10;
double d = 20.0;
Add(a, d); // 编译报错:无法确定 T 是 int 还是 double

解决这个问题有两种方式:

手动强制类型转换:Add(a, (int)d) 或 Add((double)a, d);

使用显式实例化。

2.3.2 显式实例化

显式实例化是指:在调用函数时,通过 <> 手动指定模板参数 T 的具体类型,编译器无需推演。

int main() {
    int a = 10;
    double d = 20.0;
    // 显式实例化:指定 T 为 int,编译器会将 d 隐式转换为 int
    Add<int>(a, d);
    // 显式实例化:指定 T 为 double,编译器会将 a 隐式转换为 double
    Add<double>(a, d);
    return 0;
}

显式实例化的优势是:可以解决实参类型不一致的问题,同时让代码的意图更清晰。

2.4 模板参数的匹配原则

当一个非模板函数和同名的函数模板同时存在时,编译器会按照以下规则匹配调用:

完全匹配优先:如果非模板函数的参数类型与实参完全匹配,优先调用非模板函数,不会实例化模板;

模板更匹配时优先:如果模板可以生成比非模板函数更匹配的版本,优先调用模板实例化的函数;

普通函数支持自动类型转换:非模板函数可以进行自动类型转换(如 int 转 double),但模板函数不支持。

举个例子:

// 非模板函数:专门处理 int 类型
int Add(int left, int right) {
    cout << "非模板函数:";
    return left + right;
}
// 函数模板:通用加法函数
template<typename T1, typename T2>
T1 Add(T1 left, T2 right) {
    cout << "模板函数:";
    return left + right;
}
void Test() {
    Add(1, 2); // 调用非模板函数(完全匹配)
    Add<int>(1, 2); // 调用模板实例化的函数(显式指定模板参数)
    Add(1, 2.0); // 调用模板函数(模板生成 T1=int、T2=double 的版本,更匹配)
}

运行结果:

非模板函数:3
模板函数:3
模板函数:3.0

☃. 小彩蛋:模板中::的二义性问题

在 C++ 模板中,通过类名::标识访问成员时,::后的标识可能是嵌套类型(如 typedef 重命名类型,或者内部类),也可能是静态成员变量。由于模板编译时无法提前区分这两种情况,若要表示'嵌套类型',必须用 typename 明确标记,否则编译器会因歧义报错。

template<class T>
void print(const T& con) {
    typename T::const_iterator it = con.begin();
    while (it != con.end()) {
        cout << *it << ' ';
        ++it;
    }
}

这段代码的核心错误是模板里用 T::const_iterator 时没加 typename——编译器在模板阶段分不清它是类型还是变量,必须用 typename T::const_iterator 明确标记这是类型,加上后所有连锁语法错误都会消失。


三、类模板

类模板与函数模板类似,是用于生成不同类型类的'模具'。它常用于实现容器类(如动态数组、链表、栈等),STL 中的 vector、list、queue 等容器,本质上都是类模板的实例化产物。

3.1 类模板的定义格式

类模板的定义需要在类名前声明模板参数,语法格式如下:

template<class T1, class T2, ..., class Tn>
class 类模板名 {
    // 类内成员定义(可以使用模板参数 T1、T2...Tn)
};

注意:类模板中的成员函数如果在类外定义,必须重新声明模板参数列表。

以动态顺序表(Vector)为例,类模板的实现如下:

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

// 类模板:动态顺序表
template<class T>
class Vector {
public:
    // 构造函数:默认容量为 10
    Vector(size_t capacity = 10)
        : _pData(new T[capacity])
        , _size(0)
        , _capacity(capacity) {}

    // 析构函数:类内声明,类外定义
    ~Vector();

    // 尾插元素
    void PushBack(const T& data) {
        // 容量不足时扩容(简化版扩容逻辑)
        if (_size == _capacity) {
            T* temp = new T[_capacity * 2];
            for (size_t i = 0; i < _size; ++i) {
                temp[i] = _pData[i];
            }
            delete[] _pData;
            _pData = temp;
            _capacity *= 2;
        }
        _pData[_size++] = data;
    }

    // 尾删元素
    void PopBack() {
        if (_size > 0) {
            --_size;
        }
    }

    // 获取元素个数
    size_t Size() const {
        return _size;
    }

    // 重载 [] 运算符:支持随机访问
    T& operator[](size_t pos) {
        assert(pos < _size); // 断言:pos 必须合法
        return _pData[pos];
    }

private:
    T* _pData; // 动态数组指针
    size_t _size; // 实际元素个数
    size_t _capacity; // 数组容量
};

// 类外定义析构函数:必须重新声明模板参数列表
template<class T>
Vector<T>::~Vector() {
    if (_pData) {
        delete[] _pData; // 释放动态内存
        _pData = nullptr;
        _size = 0;
        _capacity = 0;
    }
}

注意:类模板名(如 Vector)并不是真正的类,只有实例化后的类型(如 Vector<int>、Vector<double>)才是具体的类。

3.2 类模板的实例化

类模板的实例化与函数模板不同:类模板必须显式实例化,即必须在类模板名后加 <>,并指定具体类型。

int main() {
    // 实例化 int 类型的 Vector:Vector<int>是具体的类
    Vector<int> v1;
    v1.PushBack(10);
    v1.PushBack(20);
    v1.PushBack(30);
    cout << "v1 size: " << v1.Size() << endl; // 输出:3
    for (size_t i = 0; i < v1.Size(); ++i) {
        cout << v1[i] << " "; // 输出:10 20 30
    }
    cout << endl;

    // 实例化 double 类型的 Vector
    Vector<double> v2;
    v2.PushBack(3.14);
    v2.PushBack(6.28);
    cout << "v2 size: " << v2.Size() << endl; // 输出:2
    for (size_t i = 0; i < v2.Size(); ++i) {
        cout << v2[i] << " "; // 输出:3.14 6.28
    }
    cout << endl;
    return 0;
}

这里的 Vector<int> 和 Vector<double> 是两个完全独立的类,编译器会为它们分别生成对应的代码,各自的成员变量和成员函数互不干扰。


四、非类型模板参数

模板参数并非只能是'类型占位符'(如 typename T),还可以是编译期可确定的常量,这就是非类型模板参数。它允许我们在使用模板时传入常量参数,从而在编译期定制模板的行为,无需运行时计算。

4.1 核心概念与语法

类型形参:模板参数列表中跟在 class 或 typename 后的参数(如 template<class T> 中的 T),代表'待确定的类型';

非类型形参:用常量作为模板参数(如 template<class T, size_t N> 中的 N),在模板内部可直接当作常量使用。

经典案例:实现编译期定长数组

STL 中的 std::array 就是非类型模板参数的典型应用,我们可以自己实现一个简化版本:

#include <cassert>
namespace bite {
    // T:类型参数,N:非类型参数(默认值 10,编译期确定数组大小)
    template<class T, size_t N = 10>
    class Array {
    public:
        // 重载 [] 运算符,支持随机访问
        T& operator[](size_t index) {
            assert(index < N); // 编译期已知 N,索引合法性可提前校验
            return _array[index];
        }
        const T& operator[](size_t index) const {
            assert(index < N);
            return _array[index];
        }
        size_t size() const {
            return N;
        }
        // 大小是编译期常量,无运行时开销
        bool empty() const {
            return N == 0;
        }
    private:
        T _array[N]; // 非类型参数 N 直接用于数组大小定义
    };
}
4.2 关键限制

非类型模板参数有严格的语法约束,误用会直接导致编译错误,核心限制如下:

不允许的类型:浮点数(float、double)、类对象(std::string 等)、字符串字面量("hello")不能作为非类型参数;

错误示例:template<class T, double PI> class Circle {};(浮点数 PI 非法)

必须是编译期常量:非类型参数的值必须在编译期就能确定,不能是运行时变量;

错误示例:int n = 5; bite::Array<int, n> arr;(n 是运行时变量,非法)

允许的类型:必须是整型

4.3 核心优势

性能优化:数组大小、缓冲区容量等参数在编译期确定,避免动态内存分配(如 vector 的扩容开销);

类型安全:编译期校验常量合法性(如索引越界、参数类型错误),提前暴露问题;

灵活性:同一模板可通过不同常量参数生成不同配置的版本(如 Array<int,5> 和 Array<int,10> 是两个独立类型)。


五、模板特化

通用模板能处理大多数类型,但面对指针、引用等特殊类型时,可能出现逻辑错误(如比较指针地址而非指向内容)。模板特化就是为特定类型提供'定制化实现',编译器会优先选择特化版本,而非通用模板。

5.1 为什么需要特化?

先看一个反例:通用 Less 模板比较指针类型时的错误行为:

#include <iostream>
using namespace std;

// 自定义日期类
class Date {
public:
    Date(int year, int month, int day)
        : _year(year), _month(month), _day(day) {}
    bool operator<(const Date& other) const {
        return _year < other._year ||
               (_year == other._year && _month < other._month) ||
               (_year == other._year && _month == other._month && _day < other._day);
    }
private:
    int _year, _month, _day;
};

// 通用 Less 模板:比较两个值的大小
template<class T>
bool Less(T left, T right) {
    return left < right;
}

int main() {
    Date d1(2022, 7, 7), d2(2022, 7, 8);
    cout << Less(d1, d2) << endl; // 正确:比较 Date 对象,输出 1
    Date* p1 = &d1, *p2 = &d2;
    cout << Less(p1, p2) << endl; // 错误:比较指针地址,而非指向的 Date 对象
    return 0;
}

问题根源:通用模板对指针类型的处理逻辑不符合预期(我们需要比较指针指向的内容,而非地址)。此时就需要通过模板特化来修正这个问题。


5.2 函数模板特化

函数模板特化是为特定类型定制函数实现,步骤如下:

必须先有一个基础的函数模板;

关键字 template 后接一对空尖括号 <>(表示全特化);

函数名后接 <特化类型>,指定需要特化的类型;

函数形参类型必须与基础模板完全一致。

修正上述指针比较问题:

// 基础函数模板(必须先定义)
template<class T>
bool Less(T left, T right) {
    cout << "通用模板:";
    return left < right;
}

// 函数模板特化:针对 Date*类型
template<>
bool Less<Date*>(Date* left, Date* right) {
    cout << "特化模板(Date*):";
    return *left < *right; // 比较指针指向的内容
}

// 测试代码
int main() {
    Date d1(2022, 7, 7), d2(2022, 7, 8);
    Date* p1 = &d1, *p2 = &d2;
    cout << Less(d1, d2) << endl; // 输出:通用模板:1
    cout << Less(p1, p2) << endl; // 输出:特化模板(Date*):1(正确)
    return 0;
}
注意:函数模板不建议特化

如果函数模板遇到无法处理的类型,直接写非模板函数重载更简单清晰,可读性更高:

// 直接重载非模板函数,替代特化
bool Less(Date* left, Date* right) {
    return *left < *right;
}

原因:函数模板特化的语法繁琐,且参数类型必须与基础模板完全一致,容易出错;而函数重载更灵活,无需依赖基础模板。


5.3 类模板特化

类模板特化比函数模板特化更常用,分为全特化和偏特化两类,适用于 STL 容器、算法适配器等场景。

5.3.1 全特化

全特化是将模板参数列表中的所有参数都确定化,为特定类型组合提供专属实现。

// 基础类模板(两个类型参数)
template<class T1, class T2>
class Data {
public:
    Data() {
        cout << "Data<T1, T2>" << endl;
    }
private:
    T1 _d1;
    T2 _d2;
};

// 全特化:针对 T1=int,T2=char 的组合
template<>
class Data<int, char> {
public:
    Data() {
        cout << "Data<int, char>" << endl;
    }
private:
    int _d1;
    char _d2;
};

// 测试
void Test() {
    Data<double, string> d1; // 调用基础模板:输出 Data<T1, T2>
    Data<int, char> d2;      // 调用全特化版本:输出 Data<int, char>
}
5.3.2 偏特化

偏特化不是指'部分参数特化',而是对模板参数进行进一步的条件限制,有两种表现形式:

形式 1:部分参数特化

将模板参数列表中的一部分参数确定化,保留其余参数为占位符。

// 基础类模板
template<class T1, class T2>
class Data {
public:
    Data() {
        cout << "Data<T1, T2>" << endl;
    }
};

// 偏特化:第二个参数特化为 int,第一个参数保留为 T1
template<class T1>
class Data<T1, int> {
public:
    Data() {
        cout << "Data<T1, int>" << endl;
    }
};

// 测试
void Test() {
    Data<string, double> d1; // 基础模板:Data<T1, T2>
    Data<double, int> d2;    // 偏特化版本:Data<T1, int>
}
形式 2:参数类型限制

对模板参数的类型进行约束(如限制为指针、引用类型),适用于所有满足该约束的类型组合。

// 偏特化 1:两个参数均为指针类型
template<class T1, class T2>
class Data<T1*, T2*> {
public:
    Data() {
        cout << "Data<T1*, T2*>" << endl;
    }
};

// 偏特化 2:两个参数均为引用类型
template<class T1, class T2>
class Data<T1&, T2&> {
public:
    Data(const T1& d1, const T2& d2)
        : _d1(d1), _d2(d2) {
        cout << "Data<T1&, T2&>" << endl;
    }
private:
    const T1& _d1;
    const T2& _d2;
};

// 测试
void Test() {
    Data<int*, double*> d1;       // 指针偏特化:Data<T1*, T2*>
    Data<int&, double&> d2(10, 20); // 引用偏特化:Data<T1&, T2&>
}
5.3.3 类模板特化实战:修复 sort 排序指针问题

STL 的 sort 算法支持自定义比较器,当排序指针容器时,默认比较逻辑会出错(比较地址),通过类模板特化可解决:

#include <vector>
#include <algorithm>

// 基础比较器类模板
template<class T>
struct Less {
    bool operator()(const T& x, const T& y) const {
        return x < y;
    }
};

// 类模板特化:针对 Date*类型
template<>
struct Less<Date*> {
    bool operator()(Date* x, Date* y) const {
        return *x < *y; // 比较指针指向的 Date 对象
    }
};

// 测试
int main() {
    Date d1(2022, 7, 7), d2(2022, 7, 6), d3(2022, 7, 8);
    vector<Date*> v = {&d1, &d2, &d3};
    // 使用特化后的 Less<Date*>,排序指针指向的内容
    sort(v.begin(), v.end(), Less<Date*>());
    // 排序后 v 中指针指向的日期顺序:d2(2022-7-6) → d1(2022-7-7) → d3(2022-7-8)
    return 0;
}
5.4 特化优先级规则

当一个类型同时匹配多个模板版本时,编译器按'最具体优先'选择:

全特化 > 偏特化 > 基础模板


六、模板的分离编译:工程化落地避坑

在大型项目中,我们习惯将类/函数的声明放在 .h 头文件,定义放在 .cpp 源文件(分离编译模式),但模板的分离编译会导致链接错误,这是模板工程化的核心坑点。

6.1 为什么模板不能直接分离编译?

先看一个错误示例:

// a.h(声明)
template<class T>
T Add(const T& left, const T& right);

// a.cpp(定义)
#include "a.h"
template<class T>
T Add(const T& left, const T& right) {
    return left + right;
}

// main.cpp(使用)
#include "a.h"
int main() {
    Add(1, 2);           // 调用 Add<int>
    Add(1.0, 2.0);       // 调用 Add<double>
    return 0;
}

错误原因:模板实例化时机问题

编译阶段:编译器对每个源文件单独编译。a.cpp 中只有模板定义,没有具体类型的实例化(编译器不知道要生成 Add<int> 还是 Add<double>),因此不会生成任何函数代码;main.cpp 中调用 Add,但只有声明,没有定义,编译器暂时无法生成代码,仅记录'需要调用 Add<int> 和 Add<double>'。

链接阶段:链接器尝试将 main.obj 和 a.obj 合并,但 a.obj 中没有 Add<int> 和 Add<double> 的实现,导致链接错误('无法解析的外部符号')。

6.2 解决方案
方案 1:将声明和定义放在同一文件(.h 或 .hpp)

这是最常用、最推荐的方式,直接将模板的声明和定义都写在头文件中(通常命名为 .hpp,表示'模板头文件'):

// a.hpp(声明 + 定义)
template<class T>
T Add(const T& left, const T& right) {
    return left + right;
}

// main.cpp
#include "a.hpp"
// 直接包含声明和定义
int main() {
    Add(1, 2);
    Add(1.0, 2.0);
    return 0;
}

原理:main.cpp 包含 .hpp 后,编译器在编译 main.cpp 时能看到模板的完整定义,可直接根据调用类型实例化 Add<int> 和 Add<double>,避免链接错误。

方案 2:显式实例化

在模板定义文件(a.cpp)中,显式指定需要实例化的类型:

// a.cpp
#include "a.h"
template<class T>
T Add(const T& left, const T& right) {
    return left + right;
}

// 显式实例化 int 和 double 类型
template int Add<int>(const int&, const int&);
template double Add<double>(const double&, const double&);

缺点:灵活性极差,新增类型(如 long、float)时,必须手动添加显式实例化代码,不适用于通用模板。


七、模版总结

优点:

1、代码复用与效率提升:一份模板代码可适配多种类型,避免重复编写相似逻辑(比如交换 int、double 的函数无需分别重载),节省开发资源,也让迭代开发更高效 ——C++ 标准模板库(STL)正是基于模板实现的通用工具集。

2、增强代码灵活性:模板支持'泛型编程',能兼容自定义类型(只要类型支持模板中用到的操作,比如 +、< 运算符),同时结合特化、非类型参数等特性,可灵活定制不同场景的逻辑。

缺点:

1、代码膨胀与编译耗时:不同类型/参数的模板实例会生成独立的代码,可能导致可执行文件体积增大('代码膨胀');同时编译器需要处理模板实例化,会增加编译时间。

2、编译错误难定位:模板的编译错误信息通常包含大量嵌套的类型/模板参数信息,错误提示冗长且不够直观,新手往往难以快速定位问题根源。

目录

  1. 一、泛型编程
  2. 1.1 为什么需要泛型编程?
  3. 1.2 模板:泛型编程的基础
  4. 二、函数模板
  5. 2.1 函数模板的定义格式
  6. 2.2 函数模板的原理
  7. 2.3 函数模板的实例化
  8. 2.3.1 隐式实例化
  9. 2.3.2 显式实例化
  10. 2.4 模板参数的匹配原则
  11. ☃. 小彩蛋:模板中::的二义性问题
  12. 三、类模板
  13. 3.1 类模板的定义格式
  14. 3.2 类模板的实例化
  15. 四、非类型模板参数
  16. 4.1 核心概念与语法
  17. 经典案例:实现编译期定长数组
  18. 4.2 关键限制
  19. 4.3 核心优势
  20. 五、模板特化
  21. 5.1 为什么需要特化?
  22. 5.2 函数模板特化
  23. 注意:函数模板不建议特化
  24. 5.3 类模板特化
  25. 5.3.1 全特化
  26. 5.3.2 偏特化
  27. 形式 1:部分参数特化
  28. 形式 2:参数类型限制
  29. 5.3.3 类模板特化实战:修复 sort 排序指针问题
  30. 5.4 特化优先级规则
  31. 六、模板的分离编译:工程化落地避坑
  32. 6.1 为什么模板不能直接分离编译?
  33. 6.2 解决方案
  34. 方案 1:将声明和定义放在同一文件(.h 或 .hpp)
  35. 方案 2:显式实例化
  36. 七、模版总结
  37. 优点:
  38. 缺点:

更多推荐文章

查看全部
  • 基于 Web 的工业设备监测系统架构设计:非功能需求与数据访问机制
  • Ollama 与 FastGPT 本地私有化大模型部署指南
  • 深入理解 Sentinel:分布式系统流量控制与熔断降级
  • 从零开始学习大模型:第一章 大模型基础与入门指南
  • C++11/14/17 特性整理与 Effective Modern C++ 条款实践(一)
  • Go 语言实现线性同余发生器(LCG)算法
  • 自然语言处理在客户服务领域的应用与实战
  • 物联网数据采集与可视化:使用 Python 和 MQTT 构建实时监控系统
  • 12306 反爬虫策略:Python 网络请求优化实战
  • Seedance 2.0.3 新增 Serverless 适配器:飞书机器人低成本集成方案
  • Tracecat 开源 SOAR 平台核心优势与实战指南
  • 蚂蚁集团推出企业级 AI 集成解决方案
  • 网络安全攻防领域核心证书解析:CISP-PTE/PTS/IRE/IRS 详解
  • 人工智能对上位机系统的全面重塑与影响分析
  • FPGA 实现 CAN 总线接口与数据帧解析
  • 基于 FPGA 的 TDC 抖动测试系统设计
  • 2026 AI 编程工具 Agent 时代横评:Cursor vs Claude Code vs Windsurf vs Copilot
  • 云开发 Copilot:AI 如何重塑低代码开发流程
  • Java 算法:前缀和进阶应用
  • AI 产品经理核心思维与智能产品构建指南

相关免费在线工具

  • 加密/解密文本

    使用加密算法(如AES、TripleDES、Rabbit或RC4)加密和解密文本明文。 在线工具,加密/解密文本在线工具,online

  • Gemini 图片去水印

    基于开源反向 Alpha 混合算法去除 Gemini/Nano Banana 图片水印,支持批量处理与下载。 在线工具,Gemini 图片去水印在线工具,online

  • Base64 字符串编码/解码

    将字符串编码和解码为其 Base64 格式表示形式即可。 在线工具,Base64 字符串编码/解码在线工具,online

  • Base64 文件转换器

    将字符串、文件或图像转换为其 Base64 表示形式。 在线工具,Base64 文件转换器在线工具,online

  • Markdown转HTML

    将 Markdown(GFM)转为 HTML 片段,浏览器内 marked 解析;与 HTML转Markdown 互为补充。 在线工具,Markdown转HTML在线工具,online

  • HTML转Markdown

    将 HTML 片段转为 GitHub Flavored Markdown,支持标题、列表、链接、代码块与表格等;浏览器内处理,可链接预填。 在线工具,HTML转Markdown在线工具,online