2022年1月9日 星期日

[重構] Exception : 0xC00041D , 建構子不能呼叫同類別的多載建構子?

先說問題,我有好幾個建構子,彼此之間有相同的程式碼,

因此重構之後發現出現 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,可讀性大大增加,

可惜這類的能力似乎在職場中,不是會被重視與看見的能力,


沒有留言:

張貼留言

社會新鮮人如何投資?

我的觀點是,在 沒有很多 本錢 的情況下, 別寄望每個月幾千元放到股票或者最近很夯的高股息ETF就能讓你致富, 先投資自己,讓自己的本業收入提高吧。