文章来源于网络收集而来,版权归原创者所有,如有侵权请及时联系!
8.3.4 多态
通常我们会以接口(interface)、基类(base/super class)或泛型(generic)实现多态,为不同类型提供统一调用方式。特征作为这些的 “混合体”,无疑满足需求。
- 泛型:编译期多态。
- 特征:运行期多态。
静态分派
以泛型实现 “单态化”(monomorphization)静态分派(static dispatch),为目标分别创建专用版本。虽然生成更多代码,但不会降低性能,且允许内联。
trait TestTrait { fn test(&self); } impl TestTrait for i32 { fn test(&self) { println!("{}", self); } } impl TestTrait for &str { fn test(&self) { println!("{}", self); } } /* ----------------------------------------*/ // 泛型函数。 fn call<T: TestTrait>(x: T) { x.test(); } fn main() { call(123); call("abc"); }
(gdb) disass/s fn main() { call(123); => mov edi,0x7b call 0x555555567300 <demo::call<i32>> call("abc"); lea rdi,[rip+0xffffffffffffb05a] # 0x55555556246c mov esi,0x3 call 0x5555555672c0 <demo::call<&str>> }
动态分派
如果被转换为 特征对象 (trait object),那么只有在执行时才能知道 具体类型 。这不会生成多个副本,而是通过虚拟调用(virtual function calls)实现 动态分派 (dynmaic dispatch)。动态方式会禁止某些优化,禁止内联,性能确有一些影响,但更加灵活。
trait TestTrait { fn test(&self); } impl TestTrait for i32 { fn test(&self) { println!("{}", self); } } impl TestTrait for &str { fn test(&self) { println!("{}", self); } } /* ---------------------------------------------------*/ fn call(x: &dyn TestTrait) { // 将实参转换为 trait 对象。 x.test(); } fn main() { call(&123); call(&"abc"); }
基本结构:
struct trait_object { pointer: *mut (), // rdi: lea data (void*) vtable : *mut (), // rsi: vtable } struct vtable { destructor: fn(*mut ()), // 析构 size : usize, // 目标类型大小 align : usize, // 目标类型对齐 method : fn(), // 方法表 +0x18 }
并未像泛型那样生成多个副本,而是将参数转换为特征对象。
(gdb) disass/s fn main() { call(&123); => lea rdi,[rip+0xfffffffffffff85c] # 0x555555566c04 lea rsi,[rip+0x37611] # 0x55555559e9c0 call 0x555555567380 <demo::call> call(&"abc"); lea rdi,[rip+0x37625] # 0x55555559e9e0 lea rsi,[rip+0x3762e] # 0x55555559e9f0 call 0x555555567380 <demo::call> }
(gdb) x/xw 0x555555566c04 ; .pointer 0x555555566c04: 0x0000007b ; 0x7b = 123 (gdb) x/4xg 0x55555559e9c0 ; .vtable 0x55555559e9c0: 0x0000555555567470 0x0000000000000004 0x55555559e9d0: 0x0000000000000004 0x00005555555672c0 (gdb) info symbol 0x0000555555567470 core::ptr::drop_in_place<i32> (gdb) info symbol 0x00005555555672c0 <i32 as demo::TestTrait>::test
通过 vtable + 0x18
获取目标方法地址。
(gdb) disass/s 0x555555567380 fn call(x: &dyn TestTrait) { sub rsp,0x18 mov QWORD PTR [rsp+0x8],rdi ; pointer mov QWORD PTR [rsp+0x10],rsi ; vtable x.test(); call QWORD PTR [rsi+0x18] ; vtable + 0x18 }
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论