C++ 与 Go、Rust、C#:基于实践场景的语言特性对比

news2025/5/13 6:22:54

目录

 

 

​编辑

一、语法特性对比

1.1 变量声明与数据类型

1.2 函数与控制流

1.3 面向对象特性

二、性能表现对比​编辑

2.1 基准测试数据

在计算密集型任务(如 10⁷ 次加法运算)中:

在内存分配测试(10⁵ 次对象创建)中:

对于并发请求测试(模拟 1000 个并发请求):

2.2 内存管理机制

三、应用场景分析

3.1 行业分布图

3.2 典型项目案例

四、代码实践对比

4.1 并发编程示例

4.2 内存管理示例

五、学习与职业发展

5.1 学习资源推荐

5.2 职业发展趋势

六、总结


 

 

一、语法特性对比

对比维度C++GoRustC#  
变量声明int x;var x intlet x: i32;int x;  
函数定义int add(int a, int b) { return a + b; }func add(a, b int) int { return a + b }fn add(a: i32, b: i32) -> i32 { a + b }int add(int a, int b) { return a + b; }  
类与对象class MyClass { int data; };无类,结构体 + 方法struct MyStruct { data: i32 }; impl MyStruct { fn new() -> MyStruct { MyStruct { data: 0 } } }class MyClass { public int Data; }  
并发语法手动线程、锁go func() { /* code */ }()`std::thread::spawn( { /* code */ })`Task.Run(() => { /* code */ });

1.1 变量声明与数据类型

C++ 作为老牌编程语言,变量声明方式多样,既可以使用传统的 int num; 方式,也支持 auto 类型推导,例如 auto it = vec.begin(); 。C++ 拥有丰富的数据类型,包括基本数据类型、结构体、类,还支持复杂的自定义类型,并且存在隐式类型转换,但这也可能导致一些潜在的类型错误。

Go 语言的变量声明简洁明了,采用静态类型推导,如 var num int 或者更简洁的 num := 10 。Go 语言的类型系统简洁,基本数据类型有整型、浮点型、字符串等,还提供了切片(slice)、映射(map)等实用的数据结构,方便开发者进行数据处理。

Rust 的变量声明需要显式指定类型,如 let num: i32 = 5; ,可变变量则需要使用 mut 关键字,例如 let mut num = 10; 。Rust 的类型系统严格,旨在从编译阶段就避免类型相关的错误,其提供的 VecHashMap 等数据结构,在安全性和性能上都有出色表现。

C# 的变量声明与 C++ 类似,如 int num; ,同时也支持 var 进行类型推导,例如 var list = new List<int>(); 。C# 不仅支持基础数据类型和自定义类型,还提供了丰富的泛型类型和匿名类型,方便开发者编写灵活、高效的代码。

1.2 函数与控制流

C++ 支持函数重载和模板函数,开发者可以根据不同的参数类型或数量定义多个同名函数,提高代码的复用性。在控制流方面,C++ 拥有 ifswitchforwhile 等常见的控制语句,满足各种逻辑判断和循环需求 。

Go 语言没有函数重载,但支持多返回值,例如 func divide(a, b int) (int, error) { if b == 0 { return 0, fmt.Errorf("division by zero") } return a / b, nil } 。在控制流上,Go 语言的 for 循环功能强大,可以替代 while 循环,并且 Go 语言没有传统的 switch 语句,而是通过 switch 表达式实现类似功能 。

Rust 语言的函数支持模式匹配,这在处理多种不同情况时非常方便,可以替代传统的 switch 语句。Rust 还拥有丰富的闭包和高阶函数特性,使得函数式编程风格在 Rust 中也能很好地实现,例如 let add = |x, y| x + y; 。

C# 语言拥有简洁的 Lambda 表达式和强大的 LINQ(语言集成查询),极大地简化了数据查询和处理操作。在异常处理方面,C# 拥有更规范的机制,通过 try-catch-finally 语句块,能够有效地捕获和处理程序运行过程中出现的异常 。

1.3 面向对象特性

C++ 是一种完全面向对象的编程语言,支持继承、多态和虚函数等特性,并且还支持多重继承。C++ 中的类拥有构造函数和析构函数,用于对象的初始化和资源释放,例如:

class Animal {
public:
    Animal() { std::cout << "Animal created" << std::endl; }
    virtual ~Animal() { std::cout << "Animal destroyed" << std::endl; }
    virtual void speak() { std::cout << "Animal makes a sound" << std::endl; }
};

class Dog : public Animal {
public:
    Dog() { std::cout << "Dog created" << std::endl; }
    ~Dog() { std::cout << "Dog destroyed" << std::endl; }
    void speak() override { std::cout << "Dog barks" << std::endl; }
};

Go 语言没有传统意义上的类和继承,而是通过结构体嵌入来实现类似继承的功能,并且 Go 语言的接口是隐式实现的,只要结构体实现了接口的方法,就可以被视为实现了该接口。Go 语言没有构造函数和析构函数,而是通过初始化函数和资源清理函数来完成相关操作 。

Rust 语言通过 trait 来实现接口功能,结构体可以实现一个或多个 trait 。Rust 采用结构体组合的方式来替代传统的继承,使得代码更加灵活和可复用。在 Rust 中,Drop trait 类似于析构函数,用于在对象生命周期结束时释放资源 。

C# 语言支持单继承和接口,类的属性和索引器使得对象的访问更加方便和规范。C# 还支持事件驱动编程,通过定义事件和事件处理程序,能够实现对象之间的交互和响应,例如在 Windows 应用开发中广泛应用 。

二、性能表现对比

2.1 基准测试数据

在计算密集型任务(如 10⁷ 次加法运算)中:

C++ 的性能表现出色,平均耗时约 12ms 。这得益于 C++ 对底层硬件的直接操作能力和高效的代码优化机制。Go 语言在该测试中耗时约 25ms ,虽然相比 C++ 稍慢,但 Go 的并发性能优势在其他场景下更为突出。Rust 的表现与 C++ 接近,平均耗时 13ms ,凭借其严谨的类型系统和高效的编译优化,在计算任务中能发挥出强大的性能 。C# 在计算密集型任务中耗时约 30ms ,尽管 C# 经过不断优化,但由于其运行时环境和垃圾回收机制等因素,在纯计算性能上略逊于前三者 。

在内存分配测试(10⁵ 次对象创建)中:

C++ 手动管理内存,能够精确控制内存分配和释放,平均耗时 85ms 。然而,手动内存管理也增加了开发者出错的风险。Go 语言的自动垃圾回收机制虽然方便,但在大量对象创建时会产生一定的性能开销,平均耗时 150ms 。Rust 的所有权系统在编译期进行严格检查,确保内存安全的同时,性能也较为出色,平均耗时 90ms 。C# 的垃圾回收机制在内存分配测试中耗时 180ms ,相对较慢,不过在实际应用中,其垃圾回收的优化和后台处理机制可以减少对应用性能的影响 。

对于并发请求测试(模拟 1000 个并发请求):

Go 语言展现出强大的优势,平均耗时仅 220ms 。Go 的 goroutine 和 channel 机制使得并发编程变得简单高效,能够轻松处理大量并发任务。C++ 在处理并发请求时需要手动管理线程和锁,平均耗时 450ms ,虽然可以通过优化线程池等方式提高性能,但开发难度相对较高。Rust 在并发场景下的表现也不错,平均耗时 380ms ,其线程和通道机制结合所有权系统,在保证并发安全的同时,实现了较好的性能 。C# 通过 Task 并行库处理并发请求,平均耗时 500ms ,随着.NET 框架的不断升级,C# 的并发性能也在逐步提升 。

2.2 内存管理机制

语言性能特点内存管理方式
C++手动内存管理,性能极致;多线程需手动同步手动分配释放(new/delete),可使用智能指针
Go自动 GC,适合高并发;性能略逊于 C++,胜在效率自动垃圾回收
Rust无 GC,性能接近 C++;编译期保证内存安全所有权系统,编译期检查
C#JIT 编译,性能依赖优化;自动 GC,开发效率高自动垃圾回收,可实现 IDisposable 接口释放非托管资源

C++ 的内存管理主要依赖手动操作,开发者需要使用 new 操作符分配内存,使用 delete 操作符释放内存。为了避免内存泄漏和悬空指针等问题,开发者也可以使用智能指针,如 std::unique_ptrstd::shared_ptr 等。智能指针通过自动管理内存的生命周期,大大减少了手动内存管理的风险,但也会带来一定的性能开销 。

 

Go 语言采用自动垃圾回收(GC)机制,开发者无需手动释放内存,Go 的 GC 采用标记 - 清除算法,能够自动检测不再使用的对象并回收其占用的内存。这种方式极大地减轻了开发者的负担,但在某些对实时性要求极高的场景下,GC 的暂停时间可能会对应用性能产生影响 。

 

Rust 的内存管理基于所有权系统,在编译期就对内存进行严格检查,确保每个值在任一时刻都有且只有一个所有者,当所有者离开作用域时,值会被自动释放。这种机制从根本上避免了内存泄漏和数据竞争等问题,同时也保证了程序的高性能 。

 

C# 的内存管理同样依赖自动垃圾回收,.NET 运行时会自动管理对象的生命周期。C# 还提供了析构函数和终结器机制,开发者可以在对象被回收前执行一些清理操作。此外,C# 的垃圾回收机制会根据对象的生命周期和使用频率,将对象分配到不同的代(Generation)中,进行分代式垃圾回收,提高回收效率 。

三、应用场景分析

3.1 行业分布图

从应用领域的分布来看,C++ 在游戏开发领域占据主导地位,约占 35% 的市场份额 。众多知名游戏引擎,如 Unreal Engine 都采用 C++ 开发,C++ 的高性能和对底层硬件的直接控制能力,使其能够满足游戏对图形渲染、物理模拟等方面的高要求 。

Go 语言在云原生领域表现突出,约占 25% 的市场份额 。Kubernetes、Docker 等云原生领域的核心项目都大量使用 Go 语言开发。Go 语言的高并发处理能力、简洁的语法以及良好的跨平台性,使其成为云原生开发的首选语言 。

Rust 在系统安全和底层开发领域崭露头角,约占 15% 的市场份额 。Firefox 浏览器的部分组件、区块链项目 Solana 等都采用 Rust 开发。Rust 的内存安全特性和高性能,使其在对安全性和稳定性要求极高的系统级开发中具有独特优势 。

C# 在企业级开发和游戏开发(通过 Unity 引擎)领域广泛应用,约占 25% 的市场份额 。ASP.NET Core 框架用于构建企业级 Web 应用,C# 与.NET 框架的紧密结合,提供了丰富的类库和开发工具,能够快速构建稳定、可靠的企业级应用 。在游戏开发方面,Unity 引擎使用 C# 作为主要开发语言,吸引了大量游戏开发者 。

3.2 典型项目案例

语言典型应用场景代表项目
C++游戏引擎、操作系统、嵌入式Unreal Engine、Linux 内核
Go云原生、微服务、网络编程Kubernetes、Docker
Rust系统级开发、区块链、安全软件Firefox、Solana
C#企业级 Web、Unity 游戏、桌面应用ASP.NET Core、Unity 游戏

Unreal Engine 是全球知名的 3D 游戏引擎,采用 C++ 开发。C++ 的高性能和对图形硬件的直接访问能力,使得 Unreal Engine 能够实现逼真的图形渲染效果和复杂的物理模拟。同时,C++ 的面向对象特性和丰富的库支持,方便开发者进行游戏逻辑的开发和扩展 。

Kubernetes 是一个开源的容器编排平台,主要使用 Go 语言开发。Go 语言的高并发处理能力使得 Kubernetes 能够轻松管理大规模的容器集群,实现容器的自动化部署、扩展和管理。Go 语言的简洁语法和良好的代码可读性,也有助于团队协作开发和项目维护 。

Firefox 浏览器的部分组件采用 Rust 开发,尤其是涉及安全和性能关键的模块。Rust 的内存安全特性能够有效避免浏览器因内存泄漏和安全漏洞而导致的崩溃和安全风险。同时,Rust 的高性能也有助于提升浏览器的响应速度和运行效率 。

ASP.NET Core 是微软推出的开源跨平台 Web 应用框架,使用 C# 开发。C# 与.NET 框架的结合,提供了丰富的功能和工具,如依赖注入、中间件、身份验证等,能够快速构建企业级 Web 应用。C# 的面向对象特性和 LINQ 等现代语言特性,也提高了开发效率和代码质量 。

四、代码实践对比

4.1 并发编程示例

在 C++ 中进行多线程计算,需要手动创建和管理线程,并且要注意线程同步和数据共享问题。以下是一个简单的 C++ 多线程计算示例:

#include <iostream>
#include <thread>
#include <vector>

void calculate(int& result) {
    for (int i = 0; i < 1000000; ++i) {
        result += i;
    }
}

int main() {
    int sum = 0;
    std::vector<std::thread> threads;
    for (int i = 0; i < 4; ++i) {
        threads.push_back(std::thread(calculate, std::ref(sum)));
    }
    for (auto& th : threads) {
        th.join();
    }
    std::cout << "Sum: " << sum << std::endl;
    return 0;
}

我们创建了 4 个线程同时进行计算,并通过 std::ref 传递引用,确保多个线程操作的是同一个变量。最后使用 join 方法等待所有线程完成计算。

Go 语言的并发编程非常简洁,通过 goroutine 和 channel 实现高效的并发处理。下面是一个 Go 语言的 goroutine 并发求和示例:

package main
import (
    "fmt"
)

func calculate(result chan int) {
    s := 0
    for i := 0; i < 1000000; i++ {
        s += i
    }
    result <- s
}

func main() {
    result := make(chan int, 4)
    for i := 0; i < 4; i++ {
        go calculate(result)
    }
    sum := 0
    for i := 0; i < 4; i++ {
        sum += <-result
    }
    close(result)
    fmt.Println("Sum:", sum)
}

我们通过 go 关键字启动 4 个 goroutine 进行计算,每个 goroutine 计算完成后将结果通过 channel 发送出去。主程序从 channel 中接收结果并进行累加,最后关闭 channel 。

Rust 语言的并发编程通过线程和通道实现,并且结合所有权系统保证并发安全。下面是一个 Rust 线程池并发处理的示例:

use std::sync::{Arc, Mutex};
use std::thread;
use std::collections::VecDeque;
use std::sync::mpsc::{channel, Receiver, Sender};

struct ThreadPool {
    tasks: Arc<Mutex<VecDeque<Box<dyn FnOnce() + Send>>>>,
    sender: Sender<()>
}

impl ThreadPool {
    fn new(size: usize) -> ThreadPool {
        let (sender, receiver): (Sender<()>, Receiver<()>) = channel();
        let tasks = Arc::new(Mutex::new(VecDeque::new()));
        for _ in 0..size {
            let tasks_clone = Arc::clone(&tasks);
            let receiver_clone = receiver.clone();
            thread::spawn(move || {
                loop {
                    let task = {
                        let mut guard = tasks_clone.lock().unwrap();
                        guard.pop_front()
                    };
                    match task {
                        Some(task) => task(),
                        None => {
                            match receiver_clone.recv() {
                                Ok(_) => (),
                                Err(_) => break,
                            }
                        }
                    }
                }
            });
        }
        ThreadPool { tasks, sender }
    }

    fn submit<F>(&self, task: F) where F: FnOnce() + Send + 'static {
        let mut guard = self.tasks.lock().unwrap();
        guard.push_back(Box::new(task));
    }

    fn shutdown(&self) {
        drop(self.sender);
    }
}

fn main() {
    let pool = ThreadPool::new(4);
    let sum = Arc::new(Mutex::new(0));
    for _ in 0..4 {
        let sum_clone = Arc::clone(&sum);
        pool.submit(move || {
            let mut s = 0;
            for i in 0..1000000 {
                s += i;
            }
            let mut guard = sum_clone.lock().unwrap();
            *guard += s;
        });
    }
    pool.shutdown();
    let result = *sum.lock().unwrap();
    println!("Sum: {}", result);
}

我们创建了一个线程池,通过 submit 方法提交任务,线程池中的线程从任务队列中获取任务并执行。主程序通过共享变量和互斥锁实现结果的累加,最后关闭线程池 。

C# 通过 Task 并行库进行并发编程,简化了多线程编程的复杂性。下面是一个 C# Task 并行库求和的示例:

using System;
using System.Threading.Tasks;

class Program
{
    static int sum = 0;
    static void Calculate()
    {
        for (int i = 0; i < 1000000; i++)
        {
            sum += i;
        }
    }

    static void Main()
    {
        Task[] tasks = new Task[4];
        for (int i = 0; i < 4; i++)
        {
            tasks[i] = Task.Run(Calculate);
        }
        Task.WaitAll(tasks);
        Console.WriteLine("Sum: " + sum);
    }
}

我们通过 Task.Run 方法启动 4 个任务进行计算,Task.WaitAll 方法等待所有任务完成后输出结果 。

4.2 内存管理示例

在 C++ 中,动态内存分配通过 new 和 delete 操作符实现。下面是一个简单的 C++ 动态内存分配示例:

#include <iostream>
using namespace std;

int main() {
    int* ptr = new int; // 分配一个int类型的内存空间
    *ptr = 10;
    cout << "Value: " << *ptr << endl;
    delete ptr; // 释放内存
    return 0;
}

在处理数组时,则需要使用 new[] 和 delete[] ,如:

#include <iostream>
using namespace std;

int main() {
    int* arr = new int[5]; // 分配包含5个int的数组空间
    for (int i = 0; i < 5; i++) {
        arr[i] = i;
    }
    for (int i = 0; i < 5; i++) {
        cout << arr[i] << " ";
    }
    delete[] arr; // 释放数组内存
    return 0;
}

不过手动管理内存容易出现内存泄漏(忘记 delete )、悬空指针(delete 后继续使用指针)等问题。为解决这些问题,C++ 引入智能指针,如 std::unique_ptr (独占所有权)和 std::shared_ptr (共享所有权) :

#include <iostream>
#include <memory>

int main() {
    std::unique_ptr<int> uPtr(new int(20)); // 独占所有权的智能指针
    std::cout << "Value: " << *uPtr << endl;
    // 离开作用域时,uPtr自动释放内存

    std::shared_ptr<int> sPtr1(new int(30)); // 共享所有权的智能指针
    std::shared_ptr<int> sPtr2 = sPtr1; // 多个指针共享同一内存
    std::cout << "Value: " << *sPtr2 << endl;
    // 当所有共享指针超出作用域,内存自动释放
    return 0;
}

Go 语言采用自动垃圾回收机制,开发者无需手动释放内存。以下是 Go 语言中切片(动态数组)的内存管理示例 :

package main
import "fmt"

func main() {
    slice := make([]int, 0, 5) // 创建一个初始长度为0,容量为5的切片
    for i := 0; i < 5; i++ {
        slice = append(slice, i) // 向切片添加元素,容量不足时自动扩容
    }
    fmt.Println(slice)
    // 当slice变量离开作用域,其占用的内存由垃圾回收器自动回收
}

在 Go 语言中,即使频繁创建和销毁大量对象,开发者也无需担心内存泄漏问题,Go 的垃圾回收器会在后台自动运行,定期扫描不再使用的对象并回收内存。

Rust 的内存管理基于所有权系统,在编译期就确保内存安全。以下是 Rust 中字符串的所有权转移示例:

fn main() {
    let s1 = String::from("hello"); // s1拥有"hello"字符串的所有权
    let s2 = s1; // s1的所有权转移到s2,此时s1不再有效
    // println!("{}", s1); // 这行会导致编译错误,因为s1已失去所有权
    println!("{}", s2);
}

当函数传递参数时,也遵循所有权规则:

fn take_ownership(s: String) {
    println!("Received string: {}", s);
    // s离开作用域时,其占用的内存自动释放
}

fn main() {
    let s = String::from("world");
    take_ownership(s);
    // 这里s已无效,因为所有权已转移到take_ownership函数中
}

如果想要在函数传递中不转移所有权,可以使用引用(&) :

fn borrow_string(s: &String) {
    println!("Borrowed string: {}", s);
    // 引用不会转移所有权,s在函数结束后依然有效
}

fn main() {
    let s = String::from("rust");
    borrow_string(&s);
    println!("Original string: {}", s);
}

C# 的内存管理依赖自动垃圾回收机制。以下是 C# 中对象创建和垃圾回收的示例:

using System;

class MyClass {
    ~MyClass() {
        Console.WriteLine("对象被回收");
    }
}

class Program {
    static void Main() {
        MyClass obj = new MyClass();
        obj = null; // 使对象失去引用,等待垃圾回收
        GC.Collect(); // 手动触发垃圾回收(通常无需手动触发)
    }
}

C# 的垃圾回收器会自动监测对象的引用情况,当一个对象不再被任何变量引用时,它将被标记为可回收,并在适当的时候由垃圾回收器回收内存。此外,C# 还提供了 IDisposable 接口,用于在对象不再使用时及时释放非托管资源,如文件句柄、数据库连接等 :

using System;
using System.IO;

class Program {
    static void Main() {
        using (FileStream fs = new FileStream("test.txt", FileMode.Create)) {
            // 在using块内,fs对象有效
            byte[] data = System.Text.Encoding.UTF8.GetBytes("Hello, C#");
            fs.Write(data, 0, data.Length);
        } 
        // 离开using块,fs对象自动调用Dispose方法释放资源
    }
}

五、学习与职业发展

维度C++GoRustC#
学习曲线陡峭平缓极陡峭中等
学习难点模板、内存管理goroutine、channel所有权、生命周期LINQ、.NET 框架
就业方向游戏、系统、嵌入式云原生、网络系统安全、底层企业级、游戏
薪资水平(参考)较高较高

5.1 学习资源推荐

对于想要学习 Go 语言的 C++ 选手,官方文档(Documentation - The Go Programming Language )是最权威的学习资料,涵盖了语法、标准库、工具链等详细内容。经典书籍《Go 语言圣经》对 Go 语言的特性和应用进行了深入讲解,适合系统学习。在线课程方面,Udemy 上的《The Complete Go Bootcamp》课程通过大量实践案例,帮助学习者快速掌握 Go 语言开发技能。

Rust 语言的官方文档(The Rust Programming Language - The Rust Programming Language )同样是学习的首选,其交互式的学习方式能让开发者边学边实践。《Rust 程序设计语言》是 Rust 学习的经典书籍,从基础语法到高级特性都有详细阐述。《Rust By Example》则提供了丰富的代码示例,便于学习者理解和掌握 Rust 的各种概念和用法。

C# 语言的官方文档(C# 指南 - .NET 托管语言 | Microsoft Learn )内容全面,不仅有语法介绍,还包含大量的应用场景示例。《C# 高级编程》是 C# 学习的必备书籍,深入讲解了 C# 的高级特性和.NET 框架的使用。Pluralsight 上的 C# 系列课程,由微软官方专家授课,对 C# 的最新特性和企业级应用开发有深入的讲解。

5.2 职业发展趋势

从职业发展角度看,掌握 C++ 的开发者可以在游戏开发领域深耕,如成为游戏引擎开发工程师、游戏客户端开发工程师等。随着元宇宙概念的兴起,对 C++ 高性能图形开发和底层优化的需求持续增长。

Go 语言开发者在云原生领域有广阔的发展空间,可从事云原生架构师、DevOps 工程师等职位。各大互联网公司和云计算厂商对 Go 语言人才的需求日益增加,尤其是在分布式系统、微服务架构和容器编排等方面。

Rust 开发者在系统安全、区块链、底层开发等领域备受青睐。可以成为系统安全专家、区块链开发工程师等职位,负责开发高性能、高安全性的系统和应用。随着对软件安全性要求的不断提高,Rust 语言人才的市场需求也在逐步上升。

C# 开发者在企业级应用开发和游戏开发领域有稳定的职业发展路径。可以成为.NET 企业开发工程师,负责构建企业级 Web 应用、桌面应用等;也可以通过 Unity 引擎,成为游戏开发工程师,参与手机游戏、PC 游戏等的开发。

六、总结

C++ 作为一门强大的系统级编程语言,拥有深厚的技术积累和广泛的应用场景。而 Go、Rust 和 C# 各自在不同领域展现出独特的优势。Go 语言在云原生和高并发领域表现卓越,语法简洁,开发效率高;Rust 以其内存安全和高性能,在系统级开发和安全领域占据一席之地;C# 则在企业级应用和游戏开发方面拥有强大的生态支持,其中C++ 选手在选择学习新语言时,可根据自身兴趣和职业规划进行决策。若希望进入云原生和 DevOps 领域,Go 语言是不错的选择;若对系统安全和底层开发感兴趣,Rust 语言更值得深入学习;若专注于企业级应用开发或 Unity 游戏开发,C# 语言则是理想之选。无论选择哪门语言,都能在原有 C++ 基础上进一步拓展技术视野,提升自身竞争力 。

 

 

 

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

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

相关文章

如何更改默认字体:ONLYOFFICE 协作空间、桌面编辑器、文档测试示例

在处理办公文件时&#xff0c;字体对提升用户体验至关重要。本文将逐步指导您如何在 ONLYOFFICE 协作空间、桌面应用及文档测试示例中自定义默认字体&#xff0c;以满足个性化需求&#xff0c;更好地掌控文档样式。 关于 ONLYOFFICE ONLYOFFICE 是一个国际开源项目&#xff0c…

设计模式之工厂模式(二):实际案例

设计模式之工厂模式(一) 在阅读Qt网络部分源码时候&#xff0c;发现在某处运用了工厂模式&#xff0c;而且编程技巧也用的好&#xff0c;于是就想分享出来&#xff0c;供大家参考&#xff0c;理解的不对的地方请多多指点。 以下是我整理出来的类图&#xff1a; 关键说明&#x…

基于VeRL源码深度拆解字节Seed的DAPO

1. 背景与现状&#xff1a;从PPO到GRPO的技术演进 1.1 PPO算法的基础与局限 Proximal Policy Optimization&#xff08;PPO&#xff09;作为当前强化学习领域的主流算法&#xff0c;通过重要性采样比率剪裁机制将策略更新限制在先前策略的近端区域内&#xff0c;构建了稳定的…

zst-2001 历年真题 软件工程

软件工程 - 第1题 b 软件工程 - 第2题 c 软件工程 - 第3题 c 软件工程 - 第4题 b 软件工程 - 第5题 b 软件工程 - 第6题 0.未完成&#xff1a;未执行未得到目标。1.已执行&#xff1a;输入-输出实现支持2.已管理&#xff1a;过程制度化&#xff0c;项目遵…

基于WSL用MSVC编译ffmpeg7.1

在windows平台编译FFmpeg&#xff0c;网上的大部分资料都是推荐用msys2mingw进行编译。在win10平台&#xff0c;我们可以采用另一种方式&#xff0c;即wslmsvc 实现window平台的ffmpeg编译。 下面将以vs2022ubuntu22.04 为例&#xff0c;介绍此方法 0、前期准备 安装vs2022 &…

java命令行打包class为jar并运行

1.创建无包名类: 2.添加依赖jackson 3.引用依赖包 4.命令编译class文件 生成命令: javac -d out -classpath lib/jackson-core-2.13.3.jar:lib/jackson-annotations-2.13.3.jar:lib/jackson-databind-2.13.3.jar src/UdpServer.java 编译生成class文件如下 <

vue注册用户使用v-model实现数据双向绑定

定义数据模型 Login.vue //定义数据模型 const registerData ref({username: ,password: ,confirmPassword: })使用 v-model 实现数据模型的key与注册表单中的元素之间的双向绑定 <!-- 注册表单 --><el-form ref"form" size"large" autocompl…

Nacos源码—8.Nacos升级gRPC分析六

大纲 7.服务端对服务实例进行健康检查 8.服务下线如何注销注册表和客户端等信息 9.事件驱动架构源码分析 一.处理ClientChangedEvent事件 也就是同步数据到集群节点&#xff1a; public class DistroClientDataProcessor extends SmartSubscriber implements DistroDataSt…

SpringBoot 自动装配原理 自定义一个 starter

目录 1、pom.xml 文件1.1、parent 模块1.1.1、资源文件1.1.1.1、resources 标签说明1.1.1.2、从 Maven 视角&#xff1a;资源处理全流程​ 1.1.2、插件 1.2、dependencies 模块 2、启动器3、主程序3.1、SpringBootApplication 注解3.2、SpringBootConfiguration 注解3.2.1、Con…

【C++进阶篇】多态

深入探索C多态&#xff1a;静态与动态绑定的奥秘 一. 多态1.1 定义1.2 多态定义及实现1.2.1 多态构成条件1.2.1.1 实现多态两个必要条件1.2.1.2 虚函数1.2.1.3 虚函数的重写/覆盖1.2.1.4 协变1.2.1.5 析构函数重写1.2.1.6 override和final关键字1.2.1.7 重载/重写/隐藏的对⽐ 1…

《AI大模型应知应会100篇》第60篇:Pinecone 与 Milvus,向量数据库在大模型应用中的作用

第60篇&#xff1a;Pinecone与Milvus&#xff0c;向量数据库在大模型应用中的作用 摘要 本文将系统比较Pinecone与Milvus两大主流向量数据库的技术特点、性能表现和应用场景&#xff0c;提供详细的接入代码和最佳实践&#xff0c;帮助开发者为大模型应用选择并优化向量存储解…

Java学习手册:客户端负载均衡

一、客户端负载均衡的概念 客户端负载均衡是指在客户端应用程序中&#xff0c;根据一定的算法和策略&#xff0c;将请求分发到多个服务实例上。与服务端负载均衡不同&#xff0c;客户端负载均衡不需要通过专门的负载均衡设备或服务&#xff0c;而是直接在客户端进行请求的分发…

Docker私有仓库实战:官方registry镜像实战应用

抱歉抱歉&#xff0c;离职后反而更忙了&#xff0c;拖了好久&#xff0c;从4月拖到现在&#xff0c;在学习企业级方案Harbor之前&#xff0c;我们先学习下官方方案registry&#xff0c;话不多说&#xff0c;详情见下文。 注意&#xff1a;下文省略了基本认证 TLS加密&#xff…

Redis+Caffeine构建高性能二级缓存

大家好&#xff0c;我是摘星。今天为大家带来的是RedisCaffeine构建高性能二级缓存&#xff0c;废话不多说直接开始~ 目录 二级缓存架构的技术背景 1. 基础缓存架构 2. 架构演进动因 3. 二级缓存解决方案 为什么选择本地缓存&#xff1f; 1. 极速访问 2. 减少网络IO 3…

【计算机网络】NAT技术、内网穿透与代理服务器全解析:原理、应用及实践

&#x1f4da; 博主的专栏 &#x1f427; Linux | &#x1f5a5;️ C | &#x1f4ca; 数据结构 | &#x1f4a1;C 算法 | &#x1f152; C 语言 | &#x1f310; 计算机网络 上篇文章&#xff1a;以太网、MAC地址、MTU与ARP协议 下篇文章&#xff1a;五种IO模型与阻…

Python训练打卡Day21

常见的降维算法&#xff1a; # 先运行预处理阶段的代码 import pandas as pd import pandas as pd #用于数据处理和分析&#xff0c;可处理表格数据。 import numpy as np #用于数值计算&#xff0c;提供了高效的数组操作。 import matplotlib.pyplot as plt #用于绘…

node .js 启动基于express框架的后端服务报错解决

问题&#xff1a; node .js 用npm start 启动基于express框架的后端服务报错如下&#xff1a; /c/Program Files/nodejs/npm: line 65: 26880 Segmentation fault "$NODE_EXE" "$NPM_CLI_JS" "$" 原因分析&#xff1a; 遇到 /c/Program F…

并发笔记-信号量(四)

文章目录 背景与动机31.1 信号量&#xff1a;定义 (Semaphores: A Definition)31.2 二元信号量 (用作锁) (Binary Semaphores - Locks)31.3 用于排序的信号量 (Semaphores For Ordering)31.4 生产者/消费者问题 (The Producer/Consumer (Bounded Buffer) Problem)31.5 读写锁 (…

【HTOP 使用指南】:如何理解主从线程?(以 Faster-LIO 为例)

htop 是 Linux 下常用的进程监控工具&#xff0c;它比传统的 top 更友好、更直观&#xff0c;尤其在分析多线程或多进程程序时非常有用。 以下截图就是在运行 Faster-LIO 实时建图时的 htop 状态展示&#xff1a; &#x1f50d; 一、颜色说明 白色&#xff08;或亮色&#xf…

数据同步DataX任务在线演示

数据同步DataX任务在线演示 1. 登录系统 访问系统登录页面&#xff0c;输入账号密码完成身份验证。 2. 环境准备 下载datax安装包&#xff0c;并解压到安装目录 3. 集群创建 点击控制台-多集群管理 计算组件添加DataX 配置DataX引擎,Datax.local.path填写安装目录。 4. …