今天開始,「每日一題」欄目正式上線,我爭取每天在朋友圈發一個問題,邀請大家討論,然後在公眾號里公布答案。
特此聲明:這裡的每日一題,是指每日最多一題,而不是每日至少一題。
這是今天的問題:
我這裡再詳細描述一下:
首先有一個類A:
還有一個類B:
在源文件中還有個BImpl類:
在上面的代碼中,用到了pimpl模式,關於此模式的介紹可以看我的這篇文章pimpl設計模式。
其中A類和B類是暴露給外部的文件,只暴露一些應該給外部調用的接口,然後所有實現都放在內部的Impl類中。而且Impl中還包含了很多沒有對外暴露的功能。
所以這裡的大體邏輯是:對外暴露一個指針,對內使用它的impl指針。
但這裡有個問題:在上面的代碼中,impl指針是private變量,一個對象指針傳遞出去,再傳回內部,怎麼拿到它的impl指針呢?
說到這裡,可能有些朋友會說,不會有這個問題,出現這種問題肯定是設計的不合理,然而這裡我們暫且不討論設計層面的東西,單純的看看怎麼能夠解決這個問題。(由於業務的需求複雜性,導致我不得不採用這種設計,當然,更多的應該是因為我水平有限,沒有想到更好的設計。)
我發完這個問題後,有很多朋友都私聊我一起討論,最終總結出了三種答案。

方案1:友元。
把B類設置為A類的友元,然後在B中可以獲得A類的private成員,然後改變一下BImpl中func的參數,變成這樣:
Impl側接收Impl類型的指針,這樣似乎更合理一些。

方案2:強轉
因為A的impl是類A的第一個成員變量,所以類A的對象地址和成員變量impl的地址相同,利用此原則就可以直接把a指針強轉成Impl指針。
這裡可能大家還會有些疑問,如果類A中有虛函數表怎麼辦?
其實不會出現這個問題,因為pimpl模式本質就是為了隱藏實現而生。隱藏實現大體就兩種方式,接口繼承或者pimpl,用pimpl就沒必要用繼承,而用了繼承就沒必要用pimpl。
我們再假設A有虛函數表,也沒啥太大問題,大不了判斷一下A是否有虛函數(可研究下type_traits),然後改變一下指針偏移量再去做強轉,這樣也行。

方案3:還是強轉,利用二維指針轉成一維指針
原理和方案2類似。
貼一個大佬的原話:
考慮到機器有可能是32位或64位字長,long類型與機器字長是相同,所以先轉為long指針,目的是為了取其值。而此方案更妙。
其實個人認為後兩種方案沒啥區別,大家怎麼看?
往期推薦
C++服務性能優化的道與術-道篇:阿姆達爾定律
壓箱底的音視頻學習資料以及面經整理
C++ Best Practices (C++最佳實踐)翻譯與閱讀筆記
萬字長文 | STL 算法總結
2021程序喵技術年貨
【性能優化】lock-free在召回引擎中的實現
SDK開發的一些思考
定下來了!
Linux中對【庫函數】的調用進行跟蹤的 3 種【插樁】技巧
【線上問題】P1級公司故障,年終獎不保
探索CPU的調度原理
咳咳,說下開源項目這件事的進展
想拉着大家搞點事!
防禦性編程技巧
C++的全鏈路追蹤方案,稍微有點高端
喵哥吐血整理:軟件開發的51條建議