大家都知道:用乙個基類的指標指向乙個派生類的物件是合理的,然而很多人卻忽略了這樣做的大前提:必須使用規範的指標轉換過程。
最近要新增乙個功能,上層**為此新增了乙個虛函式介面。我一看,此虛函式所在的類也是新增的,底層驅動**以前沒有使用過這個類。
class idmial_ver5
};但是驅動現有乙個cdrvadaptdm類。問了問上層,他們說直接用現有的類繼承idmial_ver5這個父類,然後實現新增的ial_setrldramcondition虛函式就可以了。以後上層呼叫這個虛函式的時候,就會自動呼叫到驅動的實現**。
於是我就給cdrvadaptdm新增了乙個父類,一切看起來都很正常:
class cdrvadaptdm : public idmial, public idrvadpmodule, public idmial_ver5
搞定,編譯軟體,上板測試,結果——單板起不來!
我連上串列埠看,每次啟動中都會出現這麼個錯誤,然後單板就再次復位了:
program
exception current instruction address: 0x00000000
machine status register: 0x0008b032
condition register: 0x40004085
task: 0x4334460 "tbdptccmd"
經過一番定位,發現是在上層軟體呼叫這個新的介面時復位的。奇怪了,繼承乙個父類,實現其虛函式難道有問題嗎?打斷點看看上層在呼叫時,傳入的this指標,也確實就是cdrvadaptdm物件的指標沒錯。突然,我想到:難道是多重繼承搗的鬼?
於是,我寫了一段**,測試一下用父類的指標呼叫子類的虛函式:(g_pdrvadaptdm是cdrvadaptdm物件的全域性指標)
printf("g_pdrvadaptdm = %#x", (unsigned int)g_pdrvadaptdm);
idmial_ver5* pver5 = dynamic_cast(g_pdrvadaptdm);
printf("ver5 = %#x", (unsigned int)pver5);
執行這個函式,輸出如下:
g_pdrvadaptdm = 0x65daea0
ver5 = 0x65daeb0
經過一次自動型別轉換後的指標值,居然和原來的指標值不一樣!
這下明白問題出在哪兒了,恰恰就是因為上層在呼叫時用的是cdrvadaptdm物件的位址,而不是經過轉換後的指標,導致了呼叫出錯。
其實,對於多重繼承的子類,由於它必須要實現多個虛函式表,所以它的資料結構和多個父類的關係如下:
在編譯時,編譯器會根據父類指標的不同型別,對子類指標的值進行轉換。
這個時候的型別轉換就不僅僅是語義轉換這麼簡單的事了,還會實實在在的改變指標的值。
那麼上層呼叫時使用的指標為什麼沒有經過恰當的轉換呢?我找到上層獲取指標的**:
void* cdrvadpcomm::querydrvadp(const dword& dwclassid)
原來上層獲取到的是個void*指標!難怪沒辦法進行恰當的型別轉換。即使把cdrvadaptdm指標轉換成void*以後,再強制轉換成idmial_ver5指標,結果也不正確。因為一旦轉成void*,指標的型別資訊就全部丟掉了,只剩下乙個乾巴巴的值。
由於現有的**架構把所有的物件指標都儲存在void指標陣列裡面,我只好在一開始將轉換好的指標也加入到void指標陣列裡去,然後在上層查詢idmial_ver5的指標時,返回這個新的指標。
問題是解決了,但讓我感到不解的是:為啥非要用void指標陣列來儲存這些物件?它們又不是沒有共同基類。只要用乙個公有基類指標來訪問這些物件,再在需要派生類型別時使用dynamic_cast轉換,就可以讓編譯器的rtti特性來為我們構造防護網,比起void*轉換要安全得多。
比如,像下面這樣的**就是安全的**:
idmial_ver5* pver5 = querydrvadp(classid_drvadp_dm_ver5);
cdrvadaptdm* pdrvadaptdm = dynamic_cast(pver5);
if (pdrvadaptdm)
dynamic_cast允許把乙個基類的指標轉換成其派生類的指標(反過來當然更沒問題),前提是物件的實際型別符合派生類型別。假如實際型別不符合,轉換後的指標將會是null。dynamic_cast型別轉換是優雅的c++**唯一推薦使用的型別轉換方式。
萬惡的英語
entity en ti ty ent t n.實體 本質 存在 attribute at trib ute tr bju t n.屬性 標誌,象徵 特質,特性 定語 v.歸於,屬於 perspective per spec tive p r spekt v p s n.遠景,透視,看法 take ...
萬惡的英語
entity en ti ty ent t n.實體 本質 存在 attribute at trib ute tr bju t n.屬性 標誌,象徵 特質,特性 定語 v.歸於,屬於 perspective per spec tive p r spekt v p s n.遠景,透視,看法 take ...
萬惡的中介
中介者模式 鬥地主 using system.collections.generic region 主程式 public class 鬥地主 endregion 乙個牌局 中介者 public class mediator 倍率 public int multiple 加入 public bool ...