分享
  1. 首页
  2. 文章

探索C++与Go的接口底层实现

语鬼_7022 · · 1473 次点击 · · 开始浏览
这是一个创建于 的文章,其中的信息可能已经有所发展或是发生改变。

1、C++ 中的"接口"

C++并没有明确的"接口",一般约定继承某个类,已达到接口的"实现"。

首先我们来看下单继承的内存布局(<font color= Crimson size=4>依赖各厂商的实际实现,这里仅以微软实现为例进行说明····感谢宇宙最强IDE····</font>)

其多态主要由虚函数表(vfptr)实现 : 指针或引用调用虚函数时,在运行时由对象的虚函数表+函数声明顺序决定绑定到哪个函数上

class IDuck {
public: 
 //嘎嘎地叫 
 virtual void GaGaSpeaking() = 0; 
 
 //老爷的官步 
 virtual void OfficialWalking() = 0; 
 
private: 
 unsigned int height;
};
class DonaldDuck : public IDuck { 
public: 
 void GaGaSpeaking() { 
 std::cout << "DonaldDuck Speak" << std::endl; 
 } 
 
 void OfficialWalking() { 
 std::cout << "DonaldDuck Walk" << std::endl; 
 } 
};
int main(int argc, _TCHAR* argv[])
{
 DonaldDuck * duck = new DonaldDuck();
 
 duck->GaGaSpeaking(); 
 duck->OfficialWalking(); 
 cout << "---------- hooking --------" << endl;
 typedef void(*DuckFunc)();
 int * addr = (int*)duck;
 DuckFunc f1 = (DuckFunc)(*((int*)(*addr)));
 f1();
 DuckFunc f2 = (DuckFunc)(*((int*)(*addr)+1));
 f2();
 return 0;
}

内存布局为:


强制调用成员函数(甚至可以是私有)


多继承

多继承下其实也是类似,按继承顺序依次排列,还是看代码示例
class IDuck {
public: 
 //嘎嘎地叫 
 virtual void GaGaSpeaking() = 0; 
 //老爷的官步 
 virtual void OfficialWalking() = 0; 
private: 
 unsigned int height;
};
class IActor {
public:
 //搞笑
 virtual void MakeFun() = 0;
private:
 std::string Name;
};
class DonaldDuck : public IDuck, public IActor { 
public: 
 void GaGaSpeaking() { 
 std::cout << "DonaldDuck Speak" << std::endl; 
 } 
 
 void OfficialWalking() { 
 std::cout << "DonaldDuck Walk" << std::endl; 
 } 
 void MakeFun() {
 std::cout << "Wa HAHAHA ~~~" << endl;
 }
};
int main(int argc, _TCHAR* argv[])
{
 DonaldDuck * duck = new DonaldDuck();
 
 duck->GaGaSpeaking(); 
 duck->OfficialWalking(); 
 duck->MakeFun();
 cout << "---------- hooking --------" << endl;
 typedef void(*DuckFunc)();
 int * addr = (int*)duck;
 DuckFunc f1 = (DuckFunc)(*((int*)(*addr)));
 f1();
 DuckFunc f2 = (DuckFunc)(*((int*)(*addr)+1));
 f2();
 typedef void(*ActorFunc)();
 int * addr2 = (int*)(*(int*)((IActor*)duck));
 ActorFunc f3 = (ActorFunc)(*(int*)(addr2));
 f3();
 return 0;
}

内存布局为:


菱形继承(略)

更多请参考

2、Go中的接口

Golang将interface作为一种类型,并且不依赖继承,而是以一种类似于duck-typing的实现。所谓duck-typing,是一种动态类型风格:当一个obj走起来像鸭子、游泳起来像鸭子、叫起来也像鸭子,那么它就可以被称为鸭子。

既然Go并没有像C++那样要求主动告诉编译器需要继承哪个父类,那么是如何实现动态类型的呢?(基于Go1.6,1.7及之后版本由于nameOff不方便gdb打印)

首先,interface由两部分组成{tab, data},其中tab保存了接口的元数据,这个很重要。

type iface struct {
 tab *itab
 data unsafe.Pointer
}

itab中比较重要的有interfacetype及fun[],其中interfacetype保存了该接口需要实现哪些方法,fun[]则保存动态类型是如何实现这些方法的

type itab struct {
 inter *interfacetype
 _type *_type
 link *itab
 bad int32
 unused int32
 fun [1]uintptr // variable sized
}
type interfacetype struct {
 typ _type
 mhdr []imethod
}
type imethod struct {
 name *string
 pkgpath *string
 _type *_type
}

附:一篇经典论文中的图解


research!rsc: Go Data Structures: Interfaces
research!rsc: Go Data Structures: Interfaces

e.g. 唐老鸭的go版本

package main
import (
 "fmt"
)
type Duck interface {
 GaGaSpeaking()()
 OfficialWalking()()
}
type Actor interface {
 MakeFun()()
}
type DonaldDuck struct {
 height uint
 name string
}
func (dd *DonaldDuck) GaGaSpeaking()() {
 fmt.Println("DonaldDuck gaga")
}
func (dd *DonaldDuck) OfficialWalking()() {
 fmt.Println("DonaldDuck walk")
}
func (dd *DonaldDuck) MakeFun()() {
 fmt.Println("DonaldDuck make fun")
}
func main() {
 dd := &DonaldDuck{10, "tang lao ya" }
 var duck Duck = dd
 var actor Actor = dd
 duck.GaGaSpeaking()
 actor.MakeFun()
 dd.OfficialWalking()
}

我们用gdb调试一下
首先,看下结构类型与两个接口的内存关系



可见,duck与actor的data指针都指向dd
然后是Duck接口的方法集:



以及其动态类型的具体实现:




可以看到,都指向了tanglaoya的具体实现

再看看Actor的方法集:




及其动态类型的具体实现



3、总结

C++在代码编写时就明确了是否实现某个接口,并将接口信息附加在自己的内存中,但is-A的模式越来越限制模块间的解耦;Golang其宽松的接口充分降低了耦合的发生,但可能在代码书写无意中却实现了某个接口.. 此外,其实现可能会比较绕,容易发生其他错误(比如经典的interface与nil的比较等等)


有疑问加站长微信联系(非本文作者)

本文来自:简书

感谢作者:语鬼_7022

查看原文:探索C++与Go的接口底层实现

入群交流(和以上内容无关):加入Go大咖交流群,或添加微信:liuxiaoyan-s 备注:入群;或加QQ群:692541889

关注微信
1473 次点击
暂无回复
添加一条新回复 (您需要 后才能回复 没有账号 ?)
  • 请尽量让自己的回复能够对别人有帮助
  • 支持 Markdown 格式, **粗体**、~~删除线~~、`单行代码`
  • 支持 @ 本站用户;支持表情(输入 : 提示),见 Emoji cheat sheet
  • 图片支持拖拽、截图粘贴等方式上传

用户登录

没有账号?注册
(追記) (追記ここまで)

今日阅读排行

    加载中
(追記) (追記ここまで)

一周阅读排行

    加载中

关注我

  • 扫码关注领全套学习资料 关注微信公众号
  • 加入 QQ 群:
    • 192706294(已满)
    • 731990104(已满)
    • 798786647(已满)
    • 729884609(已满)
    • 977810755(已满)
    • 815126783(已满)
    • 812540095(已满)
    • 1006366459(已满)
    • 692541889

  • 关注微信公众号
  • 加入微信群:liuxiaoyan-s,备注入群
  • 也欢迎加入知识星球 Go粉丝们(免费)

给该专栏投稿 写篇新文章

每篇文章有总共有 5 次投稿机会

收入到我管理的专栏 新建专栏