探索C++與Go的接口底層實(shí)現(xiàn)

1、C++ 中的“接口”

C++并沒有明確的“接口”涩咖,一般約定繼承某個(gè)類海诲,已達(dá)到接口的“實(shí)現(xiàn)”。

首先我們來看下單繼承的內(nèi)存布局(依賴各廠商的實(shí)際實(shí)現(xiàn)檩互,這里僅以微軟實(shí)現(xiàn)為例進(jìn)行說明····感謝宇宙最強(qiáng)IDE····)

其多態(tài)主要由虛函數(shù)表(vfptr)實(shí)現(xiàn) : 指針或引用調(diào)用虛函數(shù)時(shí)饿肺,在運(yùn)行時(shí)由對象的虛函數(shù)表+函數(shù)聲明順序決定綁定到哪個(gè)函數(shù)上

class IDuck {
public:          
    //嘎嘎地叫    
    virtual void GaGaSpeaking() = 0;      
       
    //老爺?shù)墓俨?   
    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;
}

內(nèi)存布局為:

強(qiáng)制調(diào)用成員函數(shù)(甚至可以是私有)

多繼承

多繼承下其實(shí)也是類似,按繼承順序依次排列盾似,還是看代碼示例
class IDuck {
public:          
    //嘎嘎地叫    
    virtual void GaGaSpeaking() = 0;         
    //老爺?shù)墓俨?   
    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;
}

內(nèi)存布局為:

菱形繼承(略)

更多請參考

2敬辣、Go中的接口

Golang將interface作為一種類型,并且不依賴?yán)^承零院,而是以一種類似于duck-typing的實(shí)現(xiàn)溉跃。所謂duck-typing,是一種動態(tài)類型風(fēng)格:當(dāng)一個(gè)obj走起來像鴨子告抄、游泳起來像鴨子撰茎、叫起來也像鴨子,那么它就可以被稱為鴨子打洼。

既然Go并沒有像C++那樣要求主動告訴編譯器需要繼承哪個(gè)父類龄糊,那么是如何實(shí)現(xiàn)動態(tài)類型的呢?(基于Go1.6募疮,1.7及之后版本由于nameOff不方便gdb打印)

首先炫惩,interface由兩部分組成{tab, data},其中tab保存了接口的元數(shù)據(jù),這個(gè)很重要阿浓。

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

itab中比較重要的有interfacetype及fun[]他嚷,其中interfacetype保存了該接口需要實(shí)現(xiàn)哪些方法,fun[]則保存動態(tài)類型是如何實(shí)現(xiàn)這些方法的

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
}

附:一篇經(jīng)典論文中的圖解


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調(diào)試一下
首先,看下結(jié)構(gòu)類型與兩個(gè)接口的內(nèi)存關(guān)系

(gdb) p dd
$1 = (struct main.DonaldDuck *) 0xc82000e180
(gdb) p duck
$2 = {tab = 0x7ffff7f721c0, data = 0xc82000e180}
(gdb) p actor
$3 = {tab = 0x7ffff7f721f0, data = 0xc82000e180}

可見筋蓖,duck與actor的data指針都指向dd
然后是Duck接口的方法集:

(gdb) p duck.tab.inter.mhdr.len
$4 = 2
(gdb) p *(duck.tab.inter.mhdr.array[0].name)
$5 = {str = 0x4ff8c0 "GaGaSpeaking", len = 12}
(gdb) p *(duck.tab.inter.mhdr.array[1].name)
$6 = {str = 0x4ffb30 "OfficialWalking", len = 15}

以及其動態(tài)類型的具體實(shí)現(xiàn)(可以看到卸耘,都指向了tanglaoya的具體實(shí)現(xiàn))

(gdb) info symbol duck.tab.fun[0]
main.(*DonaldDuck).GaGaSpeaking in section .text of /home/yugui/go/go
(gdb) info symbol duck.tab.fun[1]
main.(*DonaldDuck).OfficialWalking in section .text of /home/yugui/go/go

再看看Actor的方法集:

(gdb) p actor.tab.inter.mhdr.len
$12 = 1
(gdb) p *(actor.tab.inter.mhdr.array[0].name)
$13 = {str = 0x4fcc10 "MakeFun", len = 7}

及其動態(tài)類型的具體實(shí)現(xiàn)

(gdb) info symbol actor.tab.fun[0]
main.(*DonaldDuck).MakeFun in section .text of /home/yugui/go/go

3、總結(jié)

C++在代碼編寫時(shí)就明確了是否實(shí)現(xiàn)某個(gè)接口粘咖,并將接口信息附加在自己的內(nèi)存中蚣抗,但is-A的模式越來越限制模塊間的解耦;Golang其寬松的接口充分降低了耦合的發(fā)生瓮下,但可能在代碼書寫無意中卻實(shí)現(xiàn)了某個(gè)接口.. 此外翰铡,其實(shí)現(xiàn)可能會比較繞,容易發(fā)生其他錯(cuò)誤(比如經(jīng)典的interface與nil的比較等等)

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末唱捣,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子网梢,更是在濱河造成了極大的恐慌震缭,老刑警劉巖,帶你破解...
    沈念sama閱讀 217,509評論 6 504
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件战虏,死亡現(xiàn)場離奇詭異拣宰,居然都是意外死亡,警方通過查閱死者的電腦和手機(jī)烦感,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,806評論 3 394
  • 文/潘曉璐 我一進(jìn)店門巡社,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人手趣,你說我怎么就攤上這事晌该。” “怎么了绿渣?”我有些...
    開封第一講書人閱讀 163,875評論 0 354
  • 文/不壞的土叔 我叫張陵朝群,是天一觀的道長。 經(jīng)常有香客問我中符,道長姜胖,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 58,441評論 1 293
  • 正文 為了忘掉前任淀散,我火速辦了婚禮右莱,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘档插。我一直安慰自己慢蜓,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,488評論 6 392
  • 文/花漫 我一把揭開白布郭膛。 她就那樣靜靜地躺著胀瞪,像睡著了一般。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上凄诞,一...
    開封第一講書人閱讀 51,365評論 1 302
  • 那天圆雁,我揣著相機(jī)與錄音,去河邊找鬼帆谍。 笑死伪朽,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的汛蝙。 我是一名探鬼主播烈涮,決...
    沈念sama閱讀 40,190評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼窖剑!你這毒婦竟也來了坚洽?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 39,062評論 0 276
  • 序言:老撾萬榮一對情侶失蹤西土,失蹤者是張志新(化名)和其女友劉穎讶舰,沒想到半個(gè)月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體需了,經(jīng)...
    沈念sama閱讀 45,500評論 1 314
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡跳昼,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,706評論 3 335
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了肋乍。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片鹅颊。...
    茶點(diǎn)故事閱讀 39,834評論 1 347
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖墓造,靈堂內(nèi)的尸體忽然破棺而出堪伍,到底是詐尸還是另有隱情,我是刑警寧澤觅闽,帶...
    沈念sama閱讀 35,559評論 5 345
  • 正文 年R本政府宣布杠娱,位于F島的核電站,受9級特大地震影響谱煤,放射性物質(zhì)發(fā)生泄漏摊求。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,167評論 3 328
  • 文/蒙蒙 一刘离、第九天 我趴在偏房一處隱蔽的房頂上張望室叉。 院中可真熱鬧,春花似錦硫惕、人聲如沸茧痕。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,779評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽踪旷。三九已至曼氛,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間令野,已是汗流浹背舀患。 一陣腳步聲響...
    開封第一講書人閱讀 32,912評論 1 269
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留气破,地道東北人聊浅。 一個(gè)月前我還...
    沈念sama閱讀 47,958評論 2 370
  • 正文 我出身青樓,卻偏偏與公主長得像现使,于是被迫代替她去往敵國和親低匙。 傳聞我的和親對象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,779評論 2 354

推薦閱讀更多精彩內(nèi)容

  • Spring Cloud為開發(fā)人員提供了快速構(gòu)建分布式系統(tǒng)中一些常見模式的工具(例如配置管理碳锈,服務(wù)發(fā)現(xiàn)顽冶,斷路器,智...
    卡卡羅2017閱讀 134,654評論 18 139
  • 書名:《代碼的未來》作者:松本行弘(Yukihiro Matsumoto)譯者:**周自恒 **本試讀章節(jié)摘自:『...
    Shyujikou閱讀 1,423評論 0 10
  • *面試心聲:其實(shí)這些題本人都沒怎么背,但是在上海 兩周半 面了大約10家 收到差不多3個(gè)offer,總結(jié)起來就是把...
    Dove_iOS閱讀 27,140評論 30 470
  • 1. Java基礎(chǔ)部分 基礎(chǔ)部分的順序:基本語法售碳,類相關(guān)的語法强重,內(nèi)部類的語法,繼承相關(guān)的語法团滥,異常的語法竿屹,線程的語...
    子非魚_t_閱讀 31,631評論 18 399
  • 博弈:賭上报强,博觀戰(zhàn): 賭艷羨著別人那份灑脫灸姊, 內(nèi)心叫囂著, 那份唾手可得在眼前招手秉溉, 手捅窗紙般易破力惯, 腦已熱,煙...
    懂我笑容任閱讀 478評論 1 0