先說問題,我有好幾個建構子,彼此之間有相同的程式碼,
因此重構之後發現出現 Exception : 0xC00041D,
才發現建構子是不能直接呼叫建構子的,必須用特殊的格式來呼叫,以下描述始末:
完整範例:contructorCallConstructro (後來才發現名稱打錯了XD)
有一次我拿到某個的Ethernet類別之後,
發現這個Ethernet類別已經和其他資訊以及Log類別耦合在一起了,
例如它的建構子就包含了機台編號、Log路徑,
但我只是想要利用這個Ethernet類別進行一般的通訊,因此用不到這些既定參數,
基於保留其他人使用條件,沿用大家都習慣的類別,決定撰寫建構子的多載,
讓這些用不到的參數變成預設值,以下撰寫一個範例示範:
一開始看到的Ethernet類別,只有一個建構子,耦合了機台資訊與Log物件資訊:
原本以為只有機台資訊不會用到,但後來Log也不用了,
因此寫了兩個多載函式,讓使用者視情況,不給參數就直接給預設值:
發現了許多重複的程式碼,加以重構一下:
看起來乾淨多了,於是編譯並執行,
居然在初始化初期就發生了exception,如文章開頭圖示:
"Exception : 0xC00041D 在使用者回撥時,發生未處理的例外狀況。"
查詢了許多資料,發現大多都是指向有物件被提早刪除,導致讀取時異常。
(當然還有其他原因,但許多人遇到這問題都是物件提早被刪除造成,所以先往這方向去找)
後來慢慢地單步偵錯,發現了一件神奇的事情。
呼叫Ethernet建立的過程是這樣的:
我在上層呼叫 Ethernet(),後續流程如下:
Ethernet() -> Ethernet(string LogFilePath) -> Ethernet(int machineIndex , string LogFilePath)
-> Ethernet(string LogFilePath) -> Ethernet() -> ~Etherenet()
建立Ethernet時居然跑到了解構子!?
難怪會出現 Exception : 0xC00041D,因為Ethernet物件被釋放掉了。
一開始不知道該如何解釋這個步驟是如何發生的,
便以關鍵字"建構子呼叫建構子"去搜尋,找到了幾篇資訊參考。
其中這篇也是因為進行多載後,也進行了重構,剛好提到了這個問題,
他提到:
"建構子無法像方法一樣以名稱呼叫,得透過關鍵字this或super(子類別),
且該this或super必須是該建構元的首句敘述。"
看完的當下馬上試試這樣的寫法,想當然的失敗了,
但讓我很確定C++肯定也有相同的限制,只是語法不同而已。
所以繼續的在找找其他資源,關鍵字改成英文"constructor call constructor",
找到兩篇給我解答:
Can I call a constructor from another constructor (do constructor chaining) in C++?
How to call a C++ class Constructor from another Constructor [duplicate]
在新版的C++11可以透過特殊寫法來達成相同的事情,
如下:
跟一開始比是不是簡潔許多了呢?
但文章中有提到,如果是較舊版的C++編譯器,是不支援這樣的寫法的,
只能將相同的程式碼擷取成function,讓每個建構子去呼叫了。
重構真的好有趣,看著東西被整理乾淨滿有成就感的,
近期幫一個類別重構幾個主要的功能,發現他們都有相同的CODE,
一整理起來發現每段主要功能都減少了45%的CODE,可讀性大大增加,
可惜這類的能力似乎在職場中,不是會被重視與看見的能力,
沒有留言:
張貼留言