面试题答案
一键面试指针(以Box指针为例)与引用在内存管理方面的主要不同点
- 所有权:
- Box指针:Box拥有其所指向的数据的所有权。当Box离开作用域时,它所指向的数据会被自动释放。例如:
{ let b = Box::new(5); // b拥有5的所有权 } // 这里b离开作用域,Box所指向的5会被释放
- 引用:引用不拥有数据的所有权。它只是借用数据,数据的所有权仍归原所有者。例如:
let a = 5; let r = &a; // r借用a,a仍拥有5的所有权
- 内存分配与释放:
- Box指针:Box通常在堆上分配内存(除非数据非常小,可能会进行栈上分配优化)。当Box被销毁时,它所分配的堆内存会被释放。例如:
let b = Box::new([1; 10000]); // 在堆上分配了一个包含10000个1的数组
- 引用:引用本身不分配额外的内存,它只是指向已存在的数据。它不会影响数据的内存释放,数据的释放由其所有者决定。例如:
let s = String::from("hello"); let r = &s; // r不分配新内存,只是指向s的数据
- 可变性:
- Box指针:Box本身可以是可变的(
mut
),从而允许修改其所指向的数据。例如:
let mut b = Box::new(5); *b = 6;
- 引用:分为不可变引用(
&T
)和可变引用(&mut T
)。不可变引用不能修改数据,可变引用在同一时间只能有一个,以确保数据一致性。例如:
let mut s = String::from("hello"); let r1 = &mut s; r1.push('!'); // 此时不能再创建另一个可变引用,若要创建不可变引用也不行,直到r1离开作用域
- Box指针:Box本身可以是可变的(
因区别导致不同内存行为的场景举例
假设有一个结构体MyStruct
:
struct MyStruct {
data: i32
}
- 使用Box指针的场景:
fn box_example() { let b = Box::new(MyStruct { data: 5 }); // 这里Box在堆上分配了MyStruct实例 // 函数结束时,Box离开作用域,MyStruct实例的内存被释放 }
- 使用引用的场景:
在更复杂的场景中,如果有一个函数接收Box指针并返回Box指针,Box的所有权会转移:fn ref_example() { let s = MyStruct { data: 5 }; let r = &s; // r不分配新内存,只是引用s // 函数结束时,s离开作用域,其内存被释放,r只是借用,不影响释放行为 }
而使用引用时,函数接收的只是借用:fn take_box(b: Box<MyStruct>) -> Box<MyStruct> { // b的所有权转移到函数中 let new_b = Box::new(MyStruct { data: b.data + 1 }); new_b }
这里函数fn borrow_ref(r: &MyStruct) { // r借用MyStruct实例 println!("Data: {}", r.data); }
borrow_ref
不能修改r
指向的数据(如果r
是不可变引用),也不会影响数据的内存管理,因为所有权仍在原所有者手中。而take_box
函数中,Box的所有权发生转移,新返回的Box也会在其作用域结束时释放内存。