也談「避免使用虛函式作為庫的介面」

2021-05-24 13:42:03 字數 1884 閱讀 1620

近日拜讀了陳碩大牛的文章c++ 工程實踐(5):避免使用虛函式作為庫的介面,文章的觀點認為應該避免使用c++的class純虛函式來定義api介面,並以com作為反向教材進行批判,對此本人有些不同意見,記錄在此與各位一同**。

陳碩認為c++的虛函式是以虛函式在class中定義的位置來確定其虛表的繫結位置,在class擴充的過程中,原有的虛函式位置不可以變動,因此帶來了介面擴充套件的脆弱與僵硬。虛函式位置不可以隨意變動是事實,但是給介面擴充套件造成問題不敢苟同。陳碩舉出的linux介面的例子,個人認為完全不妥,完全沒有必要使用百層的繼承,有很多種技巧可以解決這個問題,比方可以寫成這樣:

一般來說kernel級的api會有點特殊,kernel是系統最底層的基礎,在構建kernel時往往c++編譯器都還沒有,所以在實踐中通常我們不會用c++去定義kernel api,但是這不代表從理論上行不通。搞過系統底層的同學,應該對中斷入口表這類的資料結構不會陌生,所謂中斷入口表其實就是一組函式指標的陣列,跟c++的虛表在物理結構上是很類似的,要把中斷入口表對映成虛表,從理論上說是完全可以做到的。

以上這種介面擴充套件方式是以保留全部遺留函式為前提的。但是在實踐中,庫的公升級往往伴隨架構的進化,很多老的api函式需要被廢棄,或者函式的定義需要擴充和修改。此時,c-style的api定義風格就會遭遇一些困難,要把老的函式廢棄不是那麼輕鬆的。而用com介面的話就很簡單了,只要簡單的定義一套新的介面,讓新的庫帶上版本號儲存在新的dll裡就可以了。老的客戶**依然可以使用老的com介面和元件,不需要修改任何**。新的客戶**直接使用新版本的com介面,新的介面裡也不再需要保留那些已經廢棄的老版本的api函式。

這個例子我們可以看opengl vs d3d

早期的opengl 1.0的介面定義是c-style的,這種api定義風格比較僵化,在擴充套件時只能新增函式而不能輕易的刪除或修改原有的函式,而圖形介面的架構變化是非常快的,2~3年就會伴隨一次大的硬體架構的公升級。所以到了1.1之後引入了gl extension機制,不再增加新的api函式介面,而是讓客戶**自己去通過gl擴充套件機制去查詢當前硬體支援的特性,獲取新特性的函式指標後再進行呼叫,同時因為使用擴充套件機制需要程式手工查詢再取得函式指標,使用非常麻煩,所以還是增加了一些c-style的api來進行包裝和簡化。這樣雖然使得opengl可以快速的跟進硬體系統的發展,但是也導致各個硬體廠商在opengl裡隨意的新增自己的私有擴充套件,導致平台的混亂和**。到了opengl2.0時代,大家終於坐下來著手解決這個問題,利用委員會制定的arb擴充套件來統一各個廠商之間的私有擴充套件。但此時opengl的擴充套件已經比較發展的混亂和緩慢了,各種老的api及擴充套件特性原本應該被完全廢棄的也因為需要考慮相容性的緣故而不得不保留,opengl變得越來越龐大和難用。opengl這些年發展的失誤,跟使用僵化的c-style api以及採用不成熟的擴充套件機制是很有關係的。

早期的d3d只是opengl後面的乙個小跟班,其功能和影響力都遠不及opengl。但是d3d的進化速度非常的快,從95年到現在大約發布了10來個大的d3d版本。由於d3d採用了com作為其api介面,在每次架構公升級時微軟都採取了大刀闊斧式的改進,所有在硬體架構公升級中被淘汰的函式都不需要保留到下一代的d3d介面中,同時很多新的功能和介面都可以自由的新增到新的d3d版本中,因此新的d3d元件庫不需要考慮為老版本的相容性買單,老的客戶**依然可以用老的d3d元件庫執行,新的客戶**使用新的d3d元件庫來支援最新的硬體。

寫成偽**的話大致是這樣:

可以看到,每個新的d3d版本完全不需要去考慮跟老版本的相容問題,每次發布新版本的時候重新定義介面就是了。客戶**在使用是建立自己需要的d3d版本的物件就可以了。windows平台本身所存在的混亂和相容性問題,大部分是因為其閉源特性造成的,com的二進位制相容性從實踐證明來看是沒有問題的。從win95一直到現在的windows7,新平台保持了對大部分舊軟體的向下相容性,com元件不相容的現象很少見,而不採用com的普通dll倒是經常出現版本衝突問題。

暫時先寫這麼多,希望聽聽大家的意見。

也談memset函式的使用

memset 函式很多地方有介紹,多餘的就不說了。這裡給出msdn 上的一段解釋 sets buffers to a specified character.void memset void dest,int c,size t count wchar t wmemset wchar t dest,w...

也談內聯函式

內聯函式使用的 三個關鍵字為 inline inline forceinline 在下面情況下編譯器不使用內聯編譯 1 函式或者呼叫者用選項 ob0 編譯的 debug builds 預設選項 2 函式或者呼叫者使用不同的異常處理型別 c 使用一種異常處理,結構化使用一種異常處理 3 函式有可變的引...

也談分析函式over

as we know oracle從8.1.6開始提供分析函式,分析函式用於計算基於組的某種聚合值,它和聚合函式的不同之處是對於每個組返回多行,而聚合函式對於每個組只返回一行。快哭了。來看看區別 1.先建立示例表 create table empas select from scott.emp al...