C++安全指针,Rust用处何在?

文摘   2024-09-22 10:46   湖北  

点击上方蓝字 江湖评谈设为关注/星标




前言

众所周知,Rust是主打内存安全。在搞Rust的时候,会有这样一个疑问?C++既然已经引入了安全指针,那么Rust所谓的内存安全用处何在呢?岂不是功能重叠?

C++/Rust

1.普通安全指针

长期以来,Linux/Win上的各种漏洞大致归类指针悬空,栈溢出,双重释放等。这罪魁祸首即是C++的高度自由度导致的。Rust牺牲自由,换取安全。

但其实C++也是有安全指针的,C++独占指针std::unique_ptr:

#include <memory>#include <iostream>
int main() { std::unique_ptr<int> ptr1 = std::make_unique<int>(10); // 创建一个 unique_ptr std::cout << *ptr1 << std::endl; // 输出 10
// std::unique_ptr<int> ptr2 = ptr1; // 错误,unique_ptr 不允许拷贝 std::unique_ptr<int> ptr2 = std::move(ptr1); // 通过 std::move 转移所有权
if (!ptr1) { std::cout << "ptr1 is null after move" << std::endl; }}

ptr1只能拥有一个对象,可以通过move把ptr1的独占权转移到ptr2上,此时ptr1为空,ptr2拥有独占权。ptr2会在作用域范围结束后,自动释放,避免了手动释放如果遗忘的错误。也可以通过ptr2.reset()提前进行自动释放。

Rust安全指针:

fn main() {    let b = Box::new(5);    println!("b = {}", b);}

分配在堆上,没有共享拥有单一的所有权,编译规则限制严格,并且不提供类似于C++的自定义删除器(custom deleter),允许用户指定如何释放资源,以确保绝对的安全,离开作用域自动释放。

2.共享安全指针

C++提供了共享指针,多个指针可以共享一个对象,最后一个共享指针离开作用域,资源才会被释放

#include <memory>#include <iostream>
int main() { std::shared_ptr<int> ptr1 = std::make_shared<int>(20); // 创建 shared_ptr std::shared_ptr<int> ptr2 = ptr1; // 引用同一个对象
std::cout << "Count: " << ptr1.use_count() << std::endl; // 输出引用计数 2 std::cout << *ptr1 << std::endl; // 输出 20}

Rust提供了Arc<T>原子引用计数的共享指针,用于多线程环境的共享智能指针,内部通过原子操作管理引用计数,确保绝对安全。

use std::sync::Arc;use std::thread;
fn main() { let arc = Arc::new(10);
let arc_clone = Arc::clone(&arc); let handle = thread::spawn(move || { println!("arc_clone = {}", arc_clone); });
handle.join().unwrap();}

结尾

上面的对于C++/Rust两类指针的分析,其实很容易看出来。C++普通安全指针提供的指定释放资源和指定删除器,C++共享安全指针提供的计数器对于自由度的依赖依然是非常高的,对于多线程的变量竞争,循环引用,自定义规则,提供的外在API都有内存安全的风险,且它的安全指针限制级完全不够严格。这就会导致各种问题。

Rust通过严格的编译规则,所有权,生命周期,安全并发等概念锁死了内存的安全行为(比如以原子计数的模式确保计数的正确性),基本上不会出现C++所面临的风险。Rust的出现看来是必然的了。

往期精彩回顾

.NET9/Rust编译对比

Rust编译器深入


江湖评谈
记录,分享,自由。
 最新文章