討論區快速選單
知識庫快速選單
網路投保旅行平安險 討論區最近新進100則主題 傑米的攝影旅遊筆記
[ 回上頁 ] [ 討論區發言規則 ]
強制實作
更改我的閱讀文章字型大小
作者 : cxxlman(CxxlMan) C++優秀好手貼文超過1000則
[ 貼文 1003 | 人氣 3227 | 評價 1260 | 評價/貼文 1.26 | 送出評價 27 次 ] 
[ 給個讚 ]  [ 給個讚 ]  [ 回應本文 ]  [ 發表新文 ]  [ 回上頁 ] [ 回討論區列表 ] [ 回知識入口 ]
2017/7/21 下午 01:45:52
有什麼方法可以讓一個虛擬函數,在每一層繼承類別都需實作
作者 : ice_emissary(燃燒的大地) 貼文超過200則
[ 貼文 345 | 人氣 0 | 評價 1650 | 評價/貼文 4.78 | 送出評價 16 次 ] 
[ 給個讚 ]  [ 給個讚 ]  [ 回應本文 ]  [ 發表新文 ]  [ 回上頁 ] [ 回討論區列表 ] [ 回知識入口 ]
2017/7/21 下午 02:42:33
沒有,虛擬函式只要在某一層有被實做即可。
除非所有的子代都是你自己做的,否則似乎無法監控第三者製做的每一層子代。
作者 : cxxlman(CxxlMan) C++優秀好手貼文超過1000則
[ 貼文 1003 | 人氣 3227 | 評價 1260 | 評價/貼文 1.26 | 送出評價 27 次 ] 
[ 給個讚 ]  [ 給個讚 ]  [ 回應本文 ]  [ 發表新文 ]  [ 回上頁 ] [ 回討論區列表 ] [ 回知識入口 ]
2017/7/21 下午 05:05:02
為什麼沒這功能呢?c++ 應該提供才對吧,有什麼難言之隱嗎
作者 : ice_emissary(燃燒的大地) 貼文超過200則
[ 貼文 345 | 人氣 0 | 評價 1650 | 評價/貼文 4.78 | 送出評價 16 次 ] 
[ 給個讚 ]  [ 給個讚 ]  [ 回應本文 ]  [ 發表新文 ]  [ 回上頁 ] [ 回討論區列表 ] [ 回知識入口 ]
2017/7/22 上午 10:41:45
我想是因為沒有必要吧!
當然這是自己的經驗、加上對C++的了解、再加上一些臆測的判斷,至於實際上的真實原因?或是是否真有什麼難言之隱?
說實在,我也不是C++標準委員,我也不確定。

與其糾結在這,不如你告訴我們你是在什麼情境下、什麼應用場景下想要有這樣的功能?
也許我們比較能夠給出實質的意見,或也許真的能夠明白這樣的功能應有存在的必要。
作者 : cxxlman(CxxlMan) C++優秀好手貼文超過1000則
[ 貼文 1003 | 人氣 3227 | 評價 1260 | 評價/貼文 1.26 | 送出評價 27 次 ] 
[ 給個讚 ]  [ 給個讚 ]  [ 回應本文 ]  [ 發表新文 ]  [ 回上頁 ] [ 回討論區列表 ] [ 回知識入口 ]
2017/7/22 下午 12:58:12
1. 我寫了一個永緒儲存機制,希望各層的子孫類別能實作一個虛擬函數存取自己的資料

2. 假設我寫了一個 class Base,希望它的產生自己的分身,所以寫了一個虛擬函數 virtual Base *Clone(); 要各層子孫類別都要實作 new 出自己

以上兩點是我碰過的情況,只能道德勸說沒法強制要實作
作者 : ice_emissary(燃燒的大地) 貼文超過200則
[ 貼文 345 | 人氣 0 | 評價 1650 | 評價/貼文 4.78 | 送出評價 16 次 ] 
[ 給個讚 ]  [ 給個讚 ]  [ 回應本文 ]  [ 發表新文 ]  [ 回上頁 ] [ 回討論區列表 ] [ 回知識入口 ]
2017/7/22 下午 11:05:19
這樣的話,不是有複製建構子可以用嗎?
舉例來說:

class Child : protected Base
{
public:
    Child(const Child &src) {...}
};

這樣,當我需要 clone 的時候:

Child obj1;
...
Child obj2(obj1);
或者:
Child *obj3 = new Child(obj1);
作者 : cxxlman(CxxlMan) C++優秀好手貼文超過1000則
[ 貼文 1003 | 人氣 3227 | 評價 1260 | 評價/貼文 1.26 | 送出評價 27 次 ] 
[ 給個讚 ]  [ 給個讚 ]  [ 回應本文 ]  [ 發表新文 ]  [ 回上頁 ] [ 回討論區列表 ] [ 回知識入口 ]
2017/7/23 上午 01:07:38
我的意思是這樣:
class Prototype
{
public :
Prototype(){}
virtual ~Prototype(){}

virtual Prototype * Clone() = 0;
};
作者 : ice_emissary(燃燒的大地) 貼文超過200則
[ 貼文 345 | 人氣 0 | 評價 1650 | 評價/貼文 4.78 | 送出評價 16 次 ] 
[ 給個讚 ]  [ 給個讚 ]  [ 回應本文 ]  [ 發表新文 ]  [ 回上頁 ] [ 回討論區列表 ] [ 回知識入口 ]
2017/7/23 下午 03:20:27
這樣我明白你的意思了。
的確,在有些時候會需要使用基類複製子類實體,比如說在不使用模版的情況夏製做容器管理通用物件時。
也確實除了建構子外,,沒有辦法從語法上要求每一代都要重新實做某個函式。

但另一方面,也不一定所有的子代都要實做 clone 函式,因為不見得某代的類別一定可以產生出有意義的實體,比如說當他也是個虛類時。
作者 : cxxlman(CxxlMan) C++優秀好手貼文超過1000則
[ 貼文 1003 | 人氣 3227 | 評價 1260 | 評價/貼文 1.26 | 送出評價 27 次 ] 
[ 給個讚 ]  [ 給個讚 ]  [ 回應本文 ]  [ 發表新文 ]  [ 回上頁 ] [ 回討論區列表 ] [ 回知識入口 ]
2017/7/23 下午 08:22:21

>但另一方面,也不一定所有的子代都要實做 clone 函式,因為不見得某代的類別一定可以產生出有意義的實體,比如說當他也是個虛類時。

設計的目的就是要由實體物件完全複製,所以一定要實作
作者 : ice_emissary(燃燒的大地) 貼文超過200則
[ 貼文 345 | 人氣 0 | 評價 1650 | 評價/貼文 4.78 | 送出評價 16 次 ] 
[ 給個讚 ]  [ 給個讚 ]  [ 回應本文 ]  [ 發表新文 ]  [ 回上頁 ] [ 回討論區列表 ] [ 回知識入口 ]
2017/7/23 下午 09:29:04
>設計的目的就是要由實體物件完全複製,所以一定要實作

比如說我設計了一個 class Tiger,要能夠傳入你的程式來協做,那麼:
1. Tiger 必須繼承 Base。
2. Tiger 必須實做 Clone 函式。

這是已知的需求項目,單若因設計緣故我的 Tiger 不是直接繼承 Base,中間還卡了比方說 Animal 類別。
那麼,若 Animal 其實是個虛類別,它仍然需要實做 Clone 嗎?
1. Animal 不會在程式中出現任何的直接實體,因為他是虛類,所以在程式中一定只會出現 Animal 的子類。
    那麼子類可以處理好自己的 Clone 不就好了?
2. 你真要要求做出 Animal 的 Clone,由於 Animal 本身夠抽象,實在很難直接定義自身的實體 Clone 方法內容。
    最好的做法就是做一個空函式丟給子類各自搞定,那和留空 Clone 而不理會有差嗎?
作者 : cxxlman(CxxlMan) C++優秀好手貼文超過1000則
[ 貼文 1003 | 人氣 3227 | 評價 1260 | 評價/貼文 1.26 | 送出評價 27 次 ] 
[ 給個讚 ]  [ 給個讚 ]  [ 回應本文 ]  [ 發表新文 ]  [ 回上頁 ] [ 回討論區列表 ] [ 回知識入口 ]
2017/7/24 上午 05:44:06
Tiger 還有延伸類別要怎麼辦
作者 : cxxlman(CxxlMan) C++優秀好手貼文超過1000則
[ 貼文 1003 | 人氣 3227 | 評價 1260 | 評價/貼文 1.26 | 送出評價 27 次 ] 
[ 給個讚 ]  [ 給個讚 ]  [ 回應本文 ]  [ 發表新文 ]  [ 回上頁 ] [ 回討論區列表 ] [ 回知識入口 ]
2017/7/28 上午 04:49:47
假設有個關鍵字 must 用於虛擬函數,表示實作類別需要實作出來,如下類似,只是以下只能一層,而 must 不限層數,只要某個類別要作為實例物件時,發現須要實作的虛擬函數沒實作就丟出錯誤訊息,兩者做法都一樣,技術上並不難,只是不提供 must 的理由是什麼

class Prototype
{
public :
Prototype(){}
virtual ~Prototype(){}

virtual Prototype * Clone() = 0;
};
作者 : ice_emissary(燃燒的大地) 貼文超過200則
[ 貼文 345 | 人氣 0 | 評價 1650 | 評價/貼文 4.78 | 送出評價 16 次 ] 
[ 給個讚 ]  [ 給個讚 ]  [ 回應本文 ]  [ 發表新文 ]  [ 回上頁 ] [ 回討論區列表 ] [ 回知識入口 ]
2017/7/28 上午 09:03:12
先說現實,C++ 並不提供你要的 must、或類似關鍵字的支援,這是肯定的;
至於原因?我也不確實了解,也許其他知道的人可以回答。

截至 C++11 為止(C++14 還沒空研究,不過應該也差不多),對於類別需函式的限制控制用關鍵字只有兩個:override 和 final。
不過 final 剛好和你要的東西相反,一個被 final 修飾的虛函式是無法在子代再被覆寫的!
作者 : ccl0504(手動程式產生器) 貼文超過200則
[ 貼文 448 | 人氣 211 | 評價 910 | 評價/貼文 2.03 | 送出評價 2 次 ] 
[ 給個讚 ]  [ 給個讚 ]  [ 回應本文 ]  [ 發表新文 ]  [ 回上頁 ] [ 回討論區列表 ] [ 回知識入口 ]
2017/7/28 上午 09:05:23

must 可能跟繼承的概念有點衝突吧 ?

假如 Animal 繼承了 Prototype 而且實作了 Clone()
那 Tiger 繼承了 Animal 後, 其實就只需要針對與 Animal 不同的地方去改寫, 繼承的意義不就是希望 code 可以 leverage 父類別 ?


另外假如 Tiger 的 Clone 寫成
Prototype * Tiger::Clone()
{
    return Animal::Clone();
}
即使有定義 must, 還是無法強制子類別必須實作屬於它自己的 Clone()


所以應該問說, 想強迫子類別實作出哪些不同處 ?


作者 : cheg(cheg)
[ 貼文 99 | 人氣 125 | 評價 410 | 評價/貼文 4.14 | 送出評價 24 次 ] 
[ 給個讚 ]  [ 給個讚 ]  [ 回應本文 ]  [ 發表新文 ]  [ 回上頁 ] [ 回討論區列表 ] [ 回知識入口 ]
2017/7/28 下午 08:28:15
因為 must 關念上跟繼承會有衝突

繼承有介面與實作的繼承
Prototype 是讓人繼承介面 =0 就是要保證別人提供實作,
提供實作的人,重用了依據 Prototypye 所撰寫的程式碼 。
而 Prototype 的孫子繼承實作,重用了 Prototype 的孩子。

你的 must 限制別人重複利用程式碼的範圍,降低他重用的自由度。


  
作者 : cxxlman(CxxlMan) C++優秀好手貼文超過1000則
[ 貼文 1003 | 人氣 3227 | 評價 1260 | 評價/貼文 1.26 | 送出評價 27 次 ] 
[ 給個讚 ]  [ 給個讚 ]  [ 回應本文 ]  [ 發表新文 ]  [ 回上頁 ] [ 回討論區列表 ] [ 回知識入口 ]
2017/7/28 下午 08:34:16
>不過 final 剛好和你要的東西相反,一個被 final 修飾的虛函式是無法在子代再被覆寫的!

所以我才覺得奇怪,近百年的程式歷史難以數計的程式碼,我想要的東西應早就有人想要了,C++ 不提供應有個好理由吧

>must 可能跟繼承的概念有點衝突吧 ?

看不出有什麼衝突

>假如 Animal 繼承了 Prototype 而且實作了 Clone()
>那 Tiger 繼承了 Animal 後, 其實就只需要針對與 Animal 不同的地方去改寫, 繼承的意義不就是希望 code 可以 leverage 父類別 ?

所以不會有衝突啊,若多個 must 程式不就會更健壯

>
>另外假如 Tiger 的 Clone 寫成
>Prototype * Tiger::Clone()
>{
> return Animal::Clone();
>}
>即使有定義 must, 還是無法強制子類別必須實作屬於它自己的 Clone()

即使沒有 must,也有你一樣的問題,這是程式員的問題吧

>所以應該問說, 想強迫子類別實作出哪些不同處 ?

因為有必要,要確定某個虛擬函數一定會被最終類別實作的須要,程式員要怎麼做無法控制,不能做為反對的理由
作者 : cxxlman(CxxlMan) C++優秀好手貼文超過1000則
[ 貼文 1003 | 人氣 3227 | 評價 1260 | 評價/貼文 1.26 | 送出評價 27 次 ] 
[ 給個讚 ]  [ 給個讚 ]  [ 回應本文 ]  [ 發表新文 ]  [ 回上頁 ] [ 回討論區列表 ] [ 回知識入口 ]
2017/7/28 下午 08:41:18

>因為 must 關念上跟繼承會有衝突
>
>繼承有介面與實作的繼承
>Prototype 是讓人繼承介面 =0 就是要保證別人提供實作,
>提供實作的人,重用了依據 Prototypye 所撰寫的程式碼 。
>而 Prototype 的孫子繼承實作,重用了 Prototype 的孩子。
>
>你的 must 限制別人重複利用程式碼的範圍,降低他重用的自由度。

我 must 的意思是要最終類別須實作或改寫用的,不須這麼做的當然就不應該使用

作者 : cheg(cheg)
[ 貼文 99 | 人氣 125 | 評價 410 | 評價/貼文 4.14 | 送出評價 24 次 ] 
[ 給個讚 ]  [ 給個讚 ]  [ 回應本文 ]  [ 發表新文 ]  [ 回上頁 ] [ 回討論區列表 ] [ 回知識入口 ]
2017/7/28 下午 10:27:40
>我 must 的意思是要最終類別須實作或改寫用的,不須這麼做的當然就不應該使用

你定介面的意思是讓介面客戶端的邏輯可以被重用
但是欠缺某些功能,而 =0 是確保有那些功能存在
而使用那段邏輯的人,要怎麼實作是無法被約束的
如果他有必要繼層到第二層,表示他要重用第一層的內容
如果你加了 must 而他要繼承第二層,就要呼叫一次 super 然後什麼都不作,這是多此一舉
問題在於你無法約束他怎麼實作,你只是要一個功能而已,他還是可以用各種方法跳過 must
他真的一定要重作,為什麼不成為第二個 child 就好,非要成為 grandson ?

A() 根據 Prototype 寫碼,B 提供 Prototype 功能,C 繼承 Prototype 的孩子 B
A() 重用 B/C 的程式碼,C 重用 B 的程式碼,呼叫 A() 的客戶重用它的邏輯就是物件導向提供的寫碼方式
  
  
作者 : ccl0504(手動程式產生器) 貼文超過200則
[ 貼文 448 | 人氣 211 | 評價 910 | 評價/貼文 2.03 | 送出評價 2 次 ] 
[ 給個讚 ]  [ 給個讚 ]  [ 回應本文 ]  [ 發表新文 ]  [ 回上頁 ] [ 回討論區列表 ] [ 回知識入口 ]
2017/7/29 上午 02:53:32
>>假如 Animal 繼承了 Prototype 而且實作了 Clone()
>>那 Tiger 繼承了 Animal 後, 其實就只需要針對與 Animal 不同的地方去改寫, 繼承的意義不就是希望 code 可以 leverage 父類別 ?
>
>所以不會有衝突啊,若多個 must 程式不就會更健壯

 衝突的點在你即使有 must, 而 Tiger implement Clone() 但直接呼叫父類別的 Clone() 什麼事也沒做
 所以 must 是在 must 什麼事情 ? Clone() 每個子類別都有重寫, 結果功能全部相同 (都呼叫父類別)

>>must 可能跟繼承的概念有點衝突吧 ?
>
>看不出有什麼衝突
   繼承就是希望程式可以重複利用
   結果父類別定義 must, 結果子類別像上面的方式同樣的程式一直在重複寫
   
>即使沒有 must,也有你一樣的問題,這是程式員的問題吧
>
>>所以應該問說, 想強迫子類別實作出哪些不同處 ?
>
>因為有必要,要確定某個虛擬函數一定會被最終類別實作的須要,程式員要怎麼做無法控制,不能做為反對的理由

  必要在哪 ? 子類別孫類別 Clone() 全部都寫的一樣 => code 寫了一堆, 功能完全相同
  所以你還是沒回答重點, 你想強迫子類別 實作哪些不同處


  另外以進化論的觀點來看, 原始人覺得拿標槍打獵很重要, 強迫子/孫類別每個都要會拿標槍打獵..
  演化到現在, 拿標槍打獵重要嗎 ?
  所以要以現在的重要性去預測未來的重要性, 是否真的是 must ?


作者 : cxxlman(CxxlMan) C++優秀好手貼文超過1000則
[ 貼文 1003 | 人氣 3227 | 評價 1260 | 評價/貼文 1.26 | 送出評價 27 次 ] 
[ 給個讚 ]  [ 給個讚 ]  [ 回應本文 ]  [ 發表新文 ]  [ 回上頁 ] [ 回討論區列表 ] [ 回知識入口 ]
2017/7/29 上午 07:46:33
> 衝突的點在你即使有 must, 而 Tiger implement Clone() 但直接呼叫父類別的 Clone() 什麼事也沒做
> 所以 must 是在 must 什麼事情 ? Clone() 每個子類別都有重寫, 結果功能全部相同 (都呼叫父類別)

就算沒有 must,每個實作類別還是得實作 Clone(),must 只是請編譯器幫忙檢查實作類別是不是有實作 Clone() 而已,你去呼叫父類別做什麼,Clone() 就是要改寫父類別的 Clone(),怕子類沒去改寫我才提出 must,你還去呼叫父類,這和子類沒實作 Clone() 有什麼兩樣。

> 繼承就是希望程式可以重複利用
> 結果父類別定義 must, 結果子類別像上面的方式同樣的程式一直在重複寫

誰規定子類一定要去呼叫父類才叫繼承
   
>
> 必要在哪 ? 子類別孫類別 Clone() 全部都寫的一樣 => code 寫了一堆, 功能完全相同
> 所以你還是沒回答重點, 你想強迫子類別 實作哪些不同處

你醉了嗎

> 另外以進化論的觀點來看, 原始人覺得拿標槍打獵很重要, 強迫子/孫類別每個都要會拿標槍打獵..
> 演化到現在, 拿標槍打獵重要嗎 ?
> 所以要以現在的重要性去預測未來的重要性, 是否真的是 must ?

這不就突顯了 must 的必要性,如果不去改寫,永遠都拿標槍打獵
作者 : ccl0504(手動程式產生器) 貼文超過200則
[ 貼文 448 | 人氣 211 | 評價 910 | 評價/貼文 2.03 | 送出評價 2 次 ] 
[ 給個讚 ]  [ 給個讚 ]  [ 回應本文 ]  [ 發表新文 ]  [ 回上頁 ] [ 回討論區列表 ] [ 回知識入口 ]
2017/7/29 上午 08:45:06

>> 衝突的點在你即使有 must, 而 Tiger implement Clone() 但直接呼叫父類別的 Clone() 什麼事也沒做
>> 所以 must 是在 must 什麼事情 ? Clone() 每個子類別都有重寫, 結果功能全部相同 (都呼叫父類別)
>
>就算沒有 must,每個實作類別還是得實作 Clone(),must 只是請編譯器幫忙檢查實作類別是不是有實作 Clone() 而已,你去呼叫父類別做什麼,Clone() 就是要改寫父類別的 Clone(),怕子類沒去改寫我才提出 must,你還去呼叫父類,這和子類沒實作 Clone() 有什麼兩樣。
>
>> 繼承就是希望程式可以重複利用
>> 結果父類別定義 must, 結果子類別像上面的方式同樣的程式一直在重複寫
>
>誰規定子類一定要去呼叫父類才叫繼承
我沒這麼說吧 ? 你有看懂 !??


>
>>
>> 必要在哪 ? 子類別孫類別 Clone() 全部都寫的一樣 => code 寫了一堆, 功能完全相同
>> 所以你還是沒回答重點, 你想強迫子類別 實作哪些不同處
>
>你醉了嗎
是你吧 !?


>
>> 另外以進化論的觀點來看, 原始人覺得拿標槍打獵很重要, 強迫子/孫類別每個都要會拿標槍打獵..
>> 演化到現在, 拿標槍打獵重要嗎 ?
>> 所以要以現在的重要性去預測未來的重要性, 是否真的是 must ?
>
>這不就突顯了 must 的必要性,如果不去改寫,永遠都拿標槍打獵
仍看不出使用 must 其必要性
假如一個類別繼承到了 100 或是 1000 個 must, 那那個繼承的類別不就 100 個 must 都要搞懂 function 到底在做什麼 ?

> 程式員要怎麼做無法控制,不能做為反對的理由

既然無法控制程式員要怎麼做,
表示你只要求 1. 它要有 2.它一定要被改寫
1 => 可以用 virtual + 繼承
2 => 如前所述, 繼承一堆 must 以後, 還要花時間去複製貼上一堆重複的 code (可能連改都沒改), 真的對 coding 有比較容易 ?



作者 : ccl0504(手動程式產生器) 貼文超過200則
[ 貼文 448 | 人氣 211 | 評價 910 | 評價/貼文 2.03 | 送出評價 2 次 ] 
[ 給個讚 ]  [ 給個讚 ]  [ 回應本文 ]  [ 發表新文 ]  [ 回上頁 ] [ 回討論區列表 ] [ 回知識入口 ]
2017/7/29 上午 08:55:50
>> 另外以進化論的觀點來看, 原始人覺得拿標槍打獵很重要, 強迫子/孫類別每個都要會拿標槍打獵..
>> 演化到現在, 拿標槍打獵重要嗎 ?
>> 所以要以現在的重要性去預測未來的重要性, 是否真的是 must ?
>
>這不就突顯了 must 的必要性,如果不去改寫,永遠都拿標槍打獵

no,

使用 must 的情況 :
假如 root class 宣告了 拿標槍打獵(), 到了第 10 代發現不適用改寫, 但第 11, 12,... 到 100 每一代都還要把 拿標槍打獵() 拿出來寫一下的意義在哪 ? (拿來拜一下 ?)

使用 virtual 的情況 :
假如 root class 宣告了 拿標槍打獵(), 第 10 代使用發現已經不重後改寫, 第 11, 12 到 100 都可以不用去管 拿標槍打獵(), 只要專注新功能的部分



作者 : cxxlman(CxxlMan) C++優秀好手貼文超過1000則
[ 貼文 1003 | 人氣 3227 | 評價 1260 | 評價/貼文 1.26 | 送出評價 27 次 ] 
[ 給個讚 ]  [ 給個讚 ]  [ 回應本文 ]  [ 發表新文 ]  [ 回上頁 ] [ 回討論區列表 ] [ 回知識入口 ]
2017/7/29 上午 10:16:32
我說的 must 是為了必須要實作、改寫的虛擬函數而加的,對於不須一定要實作、改寫的虛擬函數硬要加上 must 才說不要實作,這是程式員的錯,不要去怪 must
作者 : ice_emissary(燃燒的大地) 貼文超過200則
[ 貼文 345 | 人氣 0 | 評價 1650 | 評價/貼文 4.78 | 送出評價 16 次 ] 
[ 給個讚 ]  [ 給個讚 ]  [ 回應本文 ]  [ 發表新文 ]  [ 回上頁 ] [ 回討論區列表 ] [ 回知識入口 ]
2017/7/29 下午 03:16:50
C++ 在制定許多特性時的首要考慮重點並不是要禁止程式師搬石頭砸自己的腳,這好像是某委員說過的話。
所以在制定標準的時候,當否些計劃中的特性可以給使用者帶來多方便、或更多組合彈性,但卻也有能力在不當的使用之下造成危害時,通常後者影響會被忽略。
所以 C++ 或者是 C 本身就要求高素質的程式設計人員,這是語言哲學本身的到錄取捨問題;另一個極端的部份可以參考 Java 或其他近代的高階語言。
雖然各種語言的設計師族群裡都會有些害群之馬,但低素質設計師在以 C++ 寫成的專案裡造成的破壞大概是所有其他程式語言所無法比較的!

上面說這麼多,就是要表達一點:C++ 通常沒有能力阻止使用者做蠢事,雖然程式庫設計者通常想儘量避免使用者做蠢事。

語法上,只有純虛函式要求子代必須要對其進行實做,至於哪一代才做,它就不管了,反正使用前有做出來就好!
後來加入的 override 和 final 關鍵字,也倒不是要禁止使用者做蠢事,主要的目的應該是要避免因為打錯字,或版本更新的緣故所造成的錯誤!

為什麼沒有提供 must 關鍵字,我想原因在前面的討論中很明顯了。
must 限縮了使用者的設計彈性,也沒有明顯帶來設計上的什麼方便性。
當然,我不敢肯定,也許以後的 C++ 會不會加入這個東西!畢竟我不是什麼大神,怎麼能真正理解大神們的想法?

未來先不說,以現狀而言,就用樓主題的 Clone 函式討論:
其實 C++ 在這方面也有樓主題到的物件複製行為在實做上容易被忽略的缺陷,而且似乎並沒有從語法上做出什麼限制措施。
比如說 T::T(const T&) 和 T& operator=(const T&) 這兩個函式,我想稍微了解 C++ 的人就知道我要說什麼,就不多解釋。
提這兩個函式是因為他們和 Clone 函式有相似特性、實做上都可能被忽略、且都未有什麼原生的限制或提醒措施。
作者 : cxxlman(CxxlMan) C++優秀好手貼文超過1000則
[ 貼文 1003 | 人氣 3227 | 評價 1260 | 評價/貼文 1.26 | 送出評價 27 次 ] 
[ 給個讚 ]  [ 給個讚 ]  [ 回應本文 ]  [ 發表新文 ]  [ 回上頁 ] [ 回討論區列表 ] [ 回知識入口 ]
2017/7/29 下午 06:53:18
class Prototype
{
public:
  Prototype() {}
  virtual ~Prototype() {}

  // 取得和實體物件相同的物件
  virtual Prototype *Clone() = 0;
};

class A:public Prototype
{
public:
  A() {}
  virtual ~A() {}

  virtual Prototype *Clone()
  {
    return new A;
  }
};


class B:public A
{
public:
  B() {}
  virtual ~B () {}
};

class B2 :public A
{
public:
  B2() {}
  virtual ~B2() {}

  virtual Prototype *Clone()
  {
    return new A; // 蠢事
  }
};

int main()
{
  Prototype *B_Obj = new B;
  Prototype *B2_Obj = new B2;

  Prototype *B_ObjErr = B_Obj->Clone();
  Prototype *B2_ObjErr = B2_Obj->Clone();
  
  return 0;
}

以上在沒有 must 會出現的問題,若有 must 則可避免 class B 的情況發生,至於 class B2 不管有沒有 must 一樣都會發生,這關 must 什麼事
作者 : cheg(cheg)
[ 貼文 99 | 人氣 125 | 評價 410 | 評價/貼文 4.14 | 送出評價 24 次 ] 
[ 給個讚 ]  [ 給個讚 ]  [ 回應本文 ]  [ 發表新文 ]  [ 回上頁 ] [ 回討論區列表 ] [ 回知識入口 ]
2017/7/29 下午 06:59:46
這個例子正好示範 must 不能解決問題,而 B 應該繼承 Prototype 而不是 A

B2 可以用 co-variant return type 不過問題不在這,問題在於寫的人要知道
  
  
作者 : cxxlman(CxxlMan) C++優秀好手貼文超過1000則
[ 貼文 1003 | 人氣 3227 | 評價 1260 | 評價/貼文 1.26 | 送出評價 27 次 ] 
[ 給個讚 ]  [ 給個讚 ]  [ 回應本文 ]  [ 發表新文 ]  [ 回上頁 ] [ 回討論區列表 ] [ 回知識入口 ]
2017/7/29 下午 07:10:12

>比如說 T::T(const T&) 和 T& operator=(const T&) 這兩個函式,我想稍微了解 C++ 的人就知道我要說什麼,就不多解釋。
>提這兩個函式是因為他們和 Clone 函式有相似特性、實做上都可能被忽略、且都未有什麼原生的限制或提醒措施。

T::T(const T&) 和 T& operator=(const T&) 只是某一層面(包括父類以上)資料的複製,clone() 是物件的完全複製
作者 : cxxlman(CxxlMan) C++優秀好手貼文超過1000則
[ 貼文 1003 | 人氣 3227 | 評價 1260 | 評價/貼文 1.26 | 送出評價 27 次 ] 
[ 給個讚 ]  [ 給個讚 ]  [ 回應本文 ]  [ 發表新文 ]  [ 回上頁 ] [ 回討論區列表 ] [ 回知識入口 ]
2017/7/29 下午 07:15:29

>這個例子正好示範 must 不能解決問題,而 B 應該繼承 Prototype 而不是 A

你沒有搞清楚在討論什麼,就是在討論 B 繼承自 A


>B2 可以用 co-variant return type 不過問題不在這,問題在於寫的人要知道

所以關 must 什麼事
作者 : cheg(cheg)
[ 貼文 99 | 人氣 125 | 評價 410 | 評價/貼文 4.14 | 送出評價 24 次 ] 
[ 給個讚 ]  [ 給個讚 ]  [ 回應本文 ]  [ 發表新文 ]  [ 回上頁 ] [ 回討論區列表 ] [ 回知識入口 ]
2017/7/29 下午 07:25:28
你自己想想看 Prototype 的目的吧
他要的是一個 Prototype *傳回值,而不是去限制別人傳回什麼,及如何實作
事實上 must 也達不到這種要求,B2 的例子正好說明如果有 must 的話他還是可以寫錯
這個例子示範的是錯誤使用繼承,事實上 B 跟 B2 都應該繼承 Prototype 而不是 A
  
作者 : cxxlman(CxxlMan) C++優秀好手貼文超過1000則
[ 貼文 1003 | 人氣 3227 | 評價 1260 | 評價/貼文 1.26 | 送出評價 27 次 ] 
[ 給個讚 ]  [ 給個讚 ]  [ 回應本文 ]  [ 發表新文 ]  [ 回上頁 ] [ 回討論區列表 ] [ 回知識入口 ]
2017/7/29 下午 07:36:27

>你自己想想看 Prototype 的目的吧
>他要的是一個 Prototype *傳回值,而不是去限制別人傳回什麼,及如何實作
>事實上 must 也達不到這種要求,B2 的例子正好說明如果有 must 的話他還是可以寫錯
>這個例子示範的是錯誤使用繼承,事實上 B 跟 B2 都應該繼承 Prototype 而不是 A

你被 介面-實作 的想法綁死了,不是在討論這個,而是在討論生物分類、資料庫...等等的繼承問題
作者 : cheg(cheg)
[ 貼文 99 | 人氣 125 | 評價 410 | 評價/貼文 4.14 | 送出評價 24 次 ] 
[ 給個讚 ]  [ 給個讚 ]  [ 回應本文 ]  [ 發表新文 ]  [ 回上頁 ] [ 回討論區列表 ] [ 回知識入口 ]
2017/7/29 下午 08:26:06
>你被 介面-實作 的想法綁死了,不是在討論這個,而是在討論生物分類、資料庫...等等的繼承問題
>

你要的目的,無非就是怕使用者忘記實作,而沿用了父類別的實作。
有了 must 可以起一個提醒的作用,至少可以來檢視一下改寫函數。
只是 must 無法確保實作者正確實作,不過我後來想想 =0 也是一樣
我是覺得有 must 也可以,至少有提醒作用,以 Clone 來說滿有用的
其他場合可能用處不大就是了。

搞不好其他語言可以用其他方式達成,這樣就不用多一個語言關鍵字
  
   

  
  
  
作者 : ccl0504(手動程式產生器) 貼文超過200則
[ 貼文 448 | 人氣 211 | 評價 910 | 評價/貼文 2.03 | 送出評價 2 次 ] 
[ 給個讚 ]  [ 給個讚 ]  [ 回應本文 ]  [ 發表新文 ]  [ 回上頁 ] [ 回討論區列表 ] [ 回知識入口 ]
2017/7/29 下午 10:26:03
>int main()
>{
> Prototype *B_Obj = new B;
> Prototype *B2_Obj = new B2;
>
> Prototype *B_ObjErr = B_Obj->Clone();
> Prototype *B2_ObjErr = B2_Obj->Clone();
>
> return 0;
>}
>
>以上在沒有 must 會出現的問題,若有 must 則可避免 class B 的情況發生,至於 class B2 不管有沒有 must 一樣都會發生,這關 must 什麼事

其實這個問題的根本原因是因為 new 後面只能接 class name 的關係, 所以你才想用 must 要求子類別一定要寫屬於自己的 new
所以假如能使用字串來 new object, 也不需要有 must 了, 是吧 ?


這類的問題有很多 implement, 找 c++ object factory 就可以找到很多
https://www.codeproject.com/Articles/567242/AplusC-b-bplusObjectplusFactory
https://stackoverflow.com/questions/4357500/c-abstract-factory-using-templates

目前看到的都需要手動註冊 (呼叫 macro )


有些語言直接使用字串就可以 new object 了

作者 : cxxlman(CxxlMan) C++優秀好手貼文超過1000則
[ 貼文 1003 | 人氣 3227 | 評價 1260 | 評價/貼文 1.26 | 送出評價 27 次 ] 
[ 給個讚 ]  [ 給個讚 ]  [ 回應本文 ]  [ 發表新文 ]  [ 回上頁 ] [ 回討論區列表 ] [ 回知識入口 ]
2017/8/1 下午 08:02:15

>我是覺得有 must 也可以,至少有提醒作用,以 Clone 來說滿有用的
>其他場合可能用處不大就是了。

以上面我貼的例子來說,Prototype 只是一個物件的代表,不能做什麼
事,須要配合訪問者模式(Visitor 請上網查看)來用,所以 Prototype
修改以下

class Prototype
{
public:
  Prototype() {}
  virtual ~Prototype() {}

  // 也須要每層實作類別實作
  void Accept(Visitor *v) = 0;

  // 取得和實體物件相同的物件
  virtual Prototype *Clone() = 0;
};

那個 Accept() 也須要 must 的支援

還有開頭提到的永緒儲存,資料是封裝的,只有類別自己才能存
取,每層類別的都得實作一個虛擬函數,這個也須要 must 的支援
作者 : cxxlman(CxxlMan) C++優秀好手貼文超過1000則
[ 貼文 1003 | 人氣 3227 | 評價 1260 | 評價/貼文 1.26 | 送出評價 27 次 ] 
[ 給個讚 ]  [ 給個讚 ]  [ 回應本文 ]  [ 發表新文 ]  [ 回上頁 ] [ 回討論區列表 ] [ 回知識入口 ]
2017/8/1 下午 08:05:25

>所以假如能使用字串來 new object, 也不需要有 must 了, 是吧 ?

是以下這樣嗎,還不是一樣

class Prototype
{
public:
  Prototype() {}
  virtual ~Prototype() {}

  // 也須要每層實作類別實作
  std::string GetName() = 0;

};

作者 : cheg(cheg)
[ 貼文 99 | 人氣 125 | 評價 410 | 評價/貼文 4.14 | 送出評價 24 次 ] 
[ 給個讚 ]  [ 給個讚 ]  [ 回應本文 ]  [ 發表新文 ]  [ 回上頁 ] [ 回討論區列表 ] [ 回知識入口 ]
2017/8/1 下午 08:51:08
>以上面我貼的例子來說,Prototype 只是一個物件的代表,不能做什麼
>事,須要配合訪問者模式(Visitor 請上網查看)來用,所以 Prototype
>修改以下
>
>class Prototype
>{
>public:
> Prototype() {}
> virtual ~Prototype() {}
>
> // 也須要每層實作類別實作
> void Accept(Visitor *v) = 0;
>
> // 取得和實體物件相同的物件
> virtual Prototype *Clone() = 0;
>};
>
>那個 Accept() 也須要 must 的支援
>
>還有開頭提到的永緒儲存,資料是封裝的,只有類別自己才能存
>取,每層類別的都得實作一個虛擬函數,這個也須要 must 的支援
>

Visitor Pattern: https://en.wikipedia.org/wiki/Visitor_pattern

新增 visitor 來替 element 動態新增函數
但是對於 accept() 的實作來說是固定的,是
採用 double-dispatch 把物件型態解析出來
讓 visitor可以實現新的演算法

如此看來,是否真的那麼需要 must 呢?
即使繼承到第二層,它一樣只要 visitor.accept(this)
不過不管怎麼說 must 還是可以起到一個提醒的作用
讓實作者多看一眼

我會覺得 Clone 很有用,是因為即使它不新增資料
你會繼承也一定會改寫函數內容,如果 new 錯物件
即使 data 不會有錯,預期的 behavior 也走樣了,
所以不能產生錯誤,才同意對 Prototype 滿有用的
  
  

作者 : cxxlman(CxxlMan) C++優秀好手貼文超過1000則
[ 貼文 1003 | 人氣 3227 | 評價 1260 | 評價/貼文 1.26 | 送出評價 27 次 ] 
[ 給個讚 ]  [ 給個讚 ]  [ 回應本文 ]  [ 發表新文 ]  [ 回上頁 ] [ 回討論區列表 ] [ 回知識入口 ]
2017/8/1 下午 09:22:07

>即使繼承到第二層,它一樣只要 visitor.accept(this)


你必須要假設每一層類別的 this 都不一樣,所以每一層都需要實作
作者 : cheg(cheg)
[ 貼文 99 | 人氣 125 | 評價 410 | 評價/貼文 4.14 | 送出評價 24 次 ] 
[ 給個讚 ]  [ 給個讚 ]  [ 回應本文 ]  [ 發表新文 ]  [ 回上頁 ] [ 回討論區列表 ] [ 回知識入口 ]
2017/8/1 下午 09:54:41
>你必須要假設每一層類別的 this 都不一樣,所以每一層都需要實作

每一層是不一樣呀,但是 Visitor.visit(this) 之後就會解析出來了

Visitor 要實作各種的 visit(XxxPrototype) 這樣才能擴充演算法,一個 Visitor 對應一個演算法

每一層都要實作 visitor.visit(this)

如果 must 的話,即使實作內容一樣,還得 super.accept(visitor) 一下,這是多餘的

當然,如果有 composite Prototype 的話,它的 accept 肯定不一樣,像 wiki 中舉例的

有 must 的話可以提醒一下它,要把它寫對,但我覺得 composite element 可能直接繼承 element 比較直接一點
 
  
作者 : ccl0504(手動程式產生器) 貼文超過200則
[ 貼文 448 | 人氣 211 | 評價 910 | 評價/貼文 2.03 | 送出評價 2 次 ] 
[ 給個讚 ]  [ 給個讚 ]  [ 回應本文 ]  [ 發表新文 ]  [ 回上頁 ] [ 回討論區列表 ] [ 回知識入口 ]
2017/8/1 下午 11:17:24

>
>>所以假如能使用字串來 new object, 也不需要有 must 了, 是吧 ?
>
>是以下這樣嗎,還不是一樣
>
>class Prototype
>{
>public:
> Prototype() {}
> virtual ~Prototype() {}
>
> // 也須要每層實作類別實作
> std::string GetName() = 0;
>
>};

我說的是像這樣子的 : obj = new("Prototype");
C++ 沒有直接 support, 不過有包裝過的

// ConsoleApplication1.cpp : Defines the entry point for the console application.
//

#include "stdafx.h"
#include <map>

class Base
{
public:
virtual ~Base() {}

virtual bool Get() = 0;
};

class DerivedA : public Base
{
public:
bool Get()
{
return true;
}
};

class DerivedB : public Base
{
public:
bool Get()
{
return false;
}
};

template <class T>
class Creator
{
public:
virtual ~Creator(){}
virtual T* Create() = 0;
};

template <class DerivedType, class BaseType>
class DerivedCreator : public Creator<BaseType>
{
public:
BaseType* Create()
{
return new DerivedType;
}
};

template <class T, class Key>
class Factory
{
public:
void Register(Key Id, Creator<T>* Fn)
{
FunctionMap[Id] = Fn;
}

T* Create(Key Id)
{
return functionMap[Id]->Create();
}

~Factory()
{
std::map<Key, Creator<T>*>::iterator i = functionMap.begin();
while (i != functionMap.end())
{
delete (*i).second;
++i;
}
}
private:
std::map<Key, Creator<T>*> functionMap;
};

int _tmain(int argc, _TCHAR* argv[])
{
//Register
Factory<Base, char*> temp;
temp.Register("DA", (Creator<Base>*)new DerivedCreator<DerivedA,Base>);
temp.Register("DB", (Creator<Base>*)new DerivedCreator<DerivedB,Base>);

//Pointer to base interface
Base* pBase = 0;

//Create and call
pBase = temp.Create("DA");
printf("DerivedA %u\n", pBase->Get());
delete pBase;

//Create and call
pBase = temp.Create("DB");
printf("DerivedB %u\n", pBase->Get());
delete pBase;

return 0;
}


作者 : cxxlman(CxxlMan) C++優秀好手貼文超過1000則
[ 貼文 1003 | 人氣 3227 | 評價 1260 | 評價/貼文 1.26 | 送出評價 27 次 ] 
[ 給個讚 ]  [ 給個讚 ]  [ 回應本文 ]  [ 發表新文 ]  [ 回上頁 ] [ 回討論區列表 ] [ 回知識入口 ]
2017/8/2 上午 11:47:03
>如果 must 的話,即使實作內容一樣,還得 super.accept(visitor) 一下,這是多餘的

不清楚你這句的意思,我直接寫出例子好了

#include <iostream>

using namespace std;

class A;
class B;
class B2;
class C;

class Visitor
{
public:
  Visitor(){}
  ~Visitor(){}

  void Visit(A *a)
  {
    cout << "this is A" << endl;
  }

  void Visit(B *b)
  {
    cout << "this is B" << endl;
  }

  void Visit(B2 *b2)
  {
    cout << "this is B2" << endl;
  }

};

class Prototype
{
public:
  Prototype() {}
  virtual ~Prototype() {}

  // 也須要每層實作類別實作
  virtual void Accept(Visitor *v) = 0;

  // 取得和實體物件相同的物件
  virtual Prototype *Clone() = 0;
};

class A :public Prototype
{
public:
  A() {}
  virtual ~A() {}

  virtual void Accept(Visitor *v)
  {
    v->Visit(this);
  }

  virtual Prototype *Clone()
  {
    return new A;
  }
};


class B :public A
{
public:
  B() {}
  virtual ~B() {}
};

class B2 :public A
{
public:
  B2() {}
  virtual ~B2() {}

  virtual void Accept(Visitor *v)
  {
    A::Accept(v); // 蠢事
  }

  virtual Prototype *Clone()
  {
    return new A; // 蠢事
  }
};

class C :public A
{
public:
  C() {}
  virtual ~C() {}

  virtual void Accept(Visitor *v)
  {
    v->Visit(this);
  }

  virtual Prototype *Clone()
  {
    return new C;
  }
};

int main()
{
  Visitor v;

  Prototype *B_Obj = new B;
  Prototype *C_Obj = new C;

  B_Obj->Accept(&v); // "this is A"
  C_Obj->Accept(&v); // "this is A"

  return 0;
}

因沒有 must,所以 class B 沒有被強制實作 Accept()

class C 雖然有實作 Accept(),但 class Visitor 中沒為
它準備 Visit()


作者 : cxxlman(CxxlMan) C++優秀好手貼文超過1000則
[ 貼文 1003 | 人氣 3227 | 評價 1260 | 評價/貼文 1.26 | 送出評價 27 次 ] 
[ 給個讚 ]  [ 給個讚 ]  [ 回應本文 ]  [ 發表新文 ]  [ 回上頁 ] [ 回討論區列表 ] [ 回知識入口 ]
2017/8/2 上午 11:53:10
>
>我說的是像這樣子的 : obj = new('Prototype');
>C++ 沒有直接 support, 不過有包裝過的

你寫的程式碼只是在建構物件,和 Clone 沒有什麼關西

不過若把 Factory 放入 Base 中,Clone 時就用這
個 Factory,這倒也是一個方法

作者 : cheg(cheg)
[ 貼文 99 | 人氣 125 | 評價 410 | 評價/貼文 4.14 | 送出評價 24 次 ] 
[ 給個讚 ]  [ 給個讚 ]  [ 回應本文 ]  [ 發表新文 ]  [ 回上頁 ] [ 回討論區列表 ] [ 回知識入口 ]
2017/8/2 上午 11:55:46
不好意思,這邊是我沒想清楚,搞錯了,確實不能用父類別的 accept
作者 : ccl0504(手動程式產生器) 貼文超過200則
[ 貼文 448 | 人氣 211 | 評價 910 | 評價/貼文 2.03 | 送出評價 2 次 ] 
[ 給個讚 ]  [ 給個讚 ]  [ 回應本文 ]  [ 發表新文 ]  [ 回上頁 ] [ 回討論區列表 ] [ 回知識入口 ]
主題發起人cxxlman註記此篇回應為最佳解答 2017/8/2 下午 08:23:56

>>我說的是像這樣子的 : obj = new('Prototype');
>>C++ 沒有直接 support, 不過有包裝過的
>
>你寫的程式碼只是在建構物件,和 Clone 沒有什麼關西
>
>不過若把 Factory 放入 Base 中,Clone 時就用這
>個 Factory,這倒也是一個方法

我想說有點出來 new object 的方式後, clone 也可以小修改就得出來了


#include "stdafx.h"
#include <map>

class Base
{
public:
     virtual ~Base() {}

    virtual bool Get() = 0;
    virtual Base *Clone() const = 0;
};

template <typename Dervied>
class Base_CRTP : public Base
{
public:
    virtual Base *Clone() const
    {
     return new Dervied(static_cast<Dervied const &>(*this));
    }
};

#define DERIVE_BASE_CRTP(Type) class Type: public Base_CRTP<Type>

//class DerivedA : public Base
DERIVE_BASE_CRTP(DerivedA)
{
public:
    bool Get()
    {
     return true;
    }
};

DERIVE_BASE_CRTP(DerivedB)
{
public:
    bool Get()
    {
     return false;
    }
};

template <class T>
class Creator
{
public:
    virtual ~Creator(){}
    virtual T* Create() = 0;
};

template <class DerivedType, class BaseType>
class DerivedCreator : public Creator<BaseType>
{
public:
    BaseType* Create()
    {
     return new DerivedType;
    }
};

template <class T, class Key>
class Factory
{
public:
    void Register(Key Id, Creator<T>* Fn)
    {
     functionMap[Id] = Fn;
    }

    T* Create(Key Id)
    {
     return functionMap[Id]->Create();
    }

    ~Factory()
    {
     std::map<Key, Creator<T>*>::iterator i = functionMap.begin();
     while (i != functionMap.end())
     {
     delete (*i).second;
     ++i;
     }
     }
private:
    std::map<Key, Creator<T>*> functionMap;
};

int _tmain(int argc, _TCHAR* argv[])
{
    //Register
    Factory<Base, char*> temp;
    temp.Register("DA", (Creator<Base>*)new DerivedCreator<DerivedA,Base>);
    temp.Register("DB", (Creator<Base>*)new DerivedCreator<DerivedB,Base>);

    //Pointer to base interface
    Base* pBase = 0;
    Base* pBase1 = 0;
    Base* pBase2 = 0;

    //Create and call
    pBase = temp.Create("DA");
    printf("DerivedA %u\n", pBase->Get());
    pBase1 = pBase->Clone();
    delete pBase;

    //Create and call
    pBase = temp.Create("DB");
    printf("DerivedB %u\n", pBase->Get());
    pBase2 = pBase->Clone();
    delete pBase;

    printf("Base1 = DerivedA %u\n", pBase1->Get());
    printf("Base2 = DerivedB %u\n", pBase2->Get());

    return 0;
}



作者 : cxxlman(CxxlMan) C++優秀好手貼文超過1000則
[ 貼文 1003 | 人氣 3227 | 評價 1260 | 評價/貼文 1.26 | 送出評價 27 次 ] 
[ 給個讚 ]  [ 給個讚 ]  [ 回應本文 ]  [ 發表新文 ]  [ 回上頁 ] [ 回討論區列表 ] [ 回知識入口 ]
2017/8/3 下午 12:11:03
感謝 ccl0504 的範例,讓我有了靈感,但就是有些複雜,要是能有 must 的支援要簡單多了,
以下是有強制實作機制的 Prototype

#include <iostream>
#include <sstream>

using namespace std;

class A;
class B;
class C;
class D;

class Visitor
{
public:
  Visitor(){}
  ~Visitor(){}

  void Visit(A *a);
  void Visit(B *b);
  void Visit(C *c);
  void Visit(D *d);
};

class Prototype
{
public:
  Prototype() {}
  virtual ~Prototype() {}

  // 永續儲存
  virtual void Save(ostream &save) = 0;
  virtual void Load(istream &load) = 0;

  // 接收訪問者物件,將自己傳給訪問者
  virtual void Accept(Visitor *v) = 0;

  // 取得和實體物件相同的物件
  virtual Prototype *Clone() = 0;
};

// Prototype 延伸類別要繼承的最上層類別
class Prototype_ROOT : virtual public Prototype
{
public:
  Prototype_ROOT() {}
  ~Prototype_ROOT() {}
};

// 為了可以強制實作及多重繼承
template <typename classification>
class Prototype_CRTP : virtual public Prototype
{
public:
  Prototype_CRTP(){}
  ~Prototype_CRTP(){}

  // 永續儲存
  virtual void Save(ostream &save) override = 0;
  virtual void Load(istream &load) override = 0;

  // 接收訪問者物件,將自己傳給訪問者
  virtual void Accept(Visitor *v) override = 0;

  // 取得和實體物件相同的物件
  virtual Prototype *Clone() override = 0;
};

#define PROTOTYPE_BASE_CRTP(Base) Base,public Prototype_CRTP<Base>

// 單層繼承
class A :public PROTOTYPE_BASE_CRTP(Prototype_ROOT)
{
  // 接收訪問者物件,將自己傳給訪問者
  virtual void Accept(Visitor *v) override
  {
    v->Visit(this);
  }

  // 取得和實體物件相同的物件
  virtual Prototype *Clone() override
  {
    return new A(*this);
  }

  // 身高
  int m_Height;

protected:
  // 永續儲存
  virtual void Save(ostream &save) override
  {
    save << m_Height << ' ';
  }
  virtual void Load(istream &load) override
  {
    load >> m_Height;
  }

public:
  A(int Height)
  {
    m_Height = Height;
  }
  A(const A &Src)
  {
    m_Height = Src.m_Height;
  }
  virtual ~A() {}

  int GetHeight() const
  {
    return m_Height;
  }
};
作者 : cxxlman(CxxlMan) C++優秀好手貼文超過1000則
[ 貼文 1003 | 人氣 3227 | 評價 1260 | 評價/貼文 1.26 | 送出評價 27 次 ] 
[ 給個讚 ]  [ 給個讚 ]  [ 回應本文 ]  [ 發表新文 ]  [ 回上頁 ] [ 回討論區列表 ] [ 回知識入口 ]
2017/8/3 下午 12:11:52
// 單層繼承
class B :public PROTOTYPE_BASE_CRTP(Prototype_ROOT)
{
  // 接收訪問者物件,將自己傳給訪問者
  virtual void Accept(Visitor *v) override
  {
    v->Visit(this);
  }

  // 取得和實體物件相同的物件
  virtual Prototype *Clone() override
  {
    return new B(*this);
  }

  // 身高
  int m_Weight;

protected:
  // 永續儲存
  virtual void Save(ostream &save) override
  {
    save << m_Weight << ' ';
  }
  virtual void Load(istream &load) override
  {
    load >> m_Weight;
  }

public:
  B(int Weight)
  {
    m_Weight = Weight;
  }
  B(const B &Src)
  {
    m_Weight = Src.m_Weight;
  }
  virtual ~B() {}

  int GetWeight() const
  {
    return m_Weight;
  }
};

// 多層單一繼承
class C :public PROTOTYPE_BASE_CRTP(A)
{
  // 接收訪問者物件,將自己傳給訪問者
  virtual void Accept(Visitor *v) override
  {
    v->Visit(this);
  }

  // 取得和實體物件相同的物件
  virtual Prototype *Clone() override
  {
    return new C(*this);
  }

  // 年紀
  int m_Age;

protected:
  // 永續儲存
  virtual void Save(ostream &save) override
  {
    A::Save(save);
    save << m_Age << ' ';
  }
  virtual void Load(istream &load) override
  {
    A::Load(load);
    load >> m_Age;
  }

public:
  C(int Age, int Height)
    :A(Height)
  {
    m_Age = Age;
  }
  C(const C &Src)
    :A(Src)
  {
    m_Age = Src.m_Age;
  }
  virtual ~C() {}

  int GetAge() const
  {
    return m_Age;
  }
};


作者 : cxxlman(CxxlMan) C++優秀好手貼文超過1000則
[ 貼文 1003 | 人氣 3227 | 評價 1260 | 評價/貼文 1.26 | 送出評價 27 次 ] 
[ 給個讚 ]  [ 給個讚 ]  [ 回應本文 ]  [ 發表新文 ]  [ 回上頁 ] [ 回討論區列表 ] [ 回知識入口 ]
2017/8/3 下午 12:13:07
// 多重繼承
class D :public PROTOTYPE_BASE_CRTP(A), public PROTOTYPE_BASE_CRTP(B)
{
  // 接收訪問者物件,將自己傳給訪問者
  virtual void Accept(Visitor *v) override
  {
    v->Visit(this);
  }

  // 取得和實體物件相同的物件
  virtual Prototype *Clone() override
  {
    return new D(*this);
  }

  // 年紀
  int m_Age;

protected:
  // 永續儲存
  virtual void Save(ostream &save) override
  {
    A::Save(save);
    B::Save(save);
    save << m_Age << ' ';
  }
  virtual void Load(istream &load) override
  {
    A::Load(load);
    B::Load(load);
    load >> m_Age;
  }

public:
  D(int Age, int Height, int Weight)
    :A(Height), B(Weight)
  {
    m_Age = Age;
  }
  D(const D &Src)
    :A(Src), B(Src)
  {
    m_Age = Src.m_Age;
  }
  virtual ~D() {}

  int GetAge() const
  {
    return m_Age;
  }
};


void Visitor::Visit(A *a)
{
  cout << "我是 class A\n"
    << "身高 " << a->GetHeight() << endl << endl;
}

void Visitor::Visit(B *b)
{
  cout << "我是 class B\n"
    << "體重 " << b->GetWeight() << endl << endl;
}

void Visitor::Visit(C *c)
{
  cout << "我是 class C\n"
    << "年紀 " << c->GetAge() << '\n'
    << "身高 " << c->GetHeight() << endl << endl;
}

void Visitor::Visit(D *d)
{
  cout << "我是 class D\n"
    << "年紀 " << d->GetAge() << '\n'
    << "身高 " << d->GetHeight() << '\n'
    << "體重 " << d->GetWeight() << endl << endl;
}

int main()
{
  Visitor v;

  Prototype *A_Obj = new A(180);
  Prototype *B_Obj = new B(100);
  Prototype *C_Obj = new C(50,170);
  Prototype *D_Obj = new D(40, 175, 98);

  A_Obj->Accept(&v);
  B_Obj->Accept(&v);
  C_Obj->Accept(&v);
  D_Obj->Accept(&v);

  Prototype *D_Obj2 = D_Obj;
  cout << "複製人... \n";
  D_Obj2->Accept(&v);

  Prototype *Big_Obj = new D(2000, 5000, 3000);
  stringstream persistent;
  Big_Obj->Save(persistent);
  D_Obj2->Load(persistent);
  cout << "偷天換日... \n";
  D_Obj2->Accept(&v);

  return 0;
}
作者 : cxxlman(CxxlMan) C++優秀好手貼文超過1000則
[ 貼文 1003 | 人氣 3227 | 評價 1260 | 評價/貼文 1.26 | 送出評價 27 次 ] 
[ 給個讚 ]  [ 給個讚 ]  [ 回應本文 ]  [ 發表新文 ]  [ 回上頁 ] [ 回討論區列表 ] [ 回知識入口 ]
2017/8/3 下午 12:14:57
執行結果:

我是 class A
身高 180

我是 class B
體重 100

我是 class C
年紀 50
身高 170

我是 class D
年紀 40
身高 175
體重 98

複製人...
我是 class D
年紀 40
身高 175
體重 98

偷天換日...
我是 class D
年紀 2000
身高 5000
體重 3000

作者 : cxxlman(CxxlMan) C++優秀好手貼文超過1000則
[ 貼文 1003 | 人氣 3227 | 評價 1260 | 評價/貼文 1.26 | 送出評價 27 次 ] 
[ 給個讚 ]  [ 給個讚 ]  [ 回應本文 ]  [ 發表新文 ]  [ 回上頁 ] [ 回討論區列表 ] [ 回知識入口 ]
2017/8/3 下午 12:24:18

>
>>>我說的是像這樣子的 : obj = new('Prototype');
>>>C++ 沒有直接 support, 不過有包裝過的
>>
>>你寫的程式碼只是在建構物件,和 Clone 沒有什麼關西
>>
>>不過若把 Factory 放入 Base 中,Clone 時就用這
>>個 Factory,這倒也是一個方法
>
>我想說有點出來 new object 的方式後, clone 也可以小修改就得出來了

你的方法並不是為多層強制實作,不過你給我了靈感,讓我做出來了,感謝
作者 : cxxlman(CxxlMan) C++優秀好手貼文超過1000則
[ 貼文 1003 | 人氣 3227 | 評價 1260 | 評價/貼文 1.26 | 送出評價 27 次 ] 
[ 給個讚 ]  [ 給個讚 ]  [ 回應本文 ]  [ 發表新文 ]  [ 回上頁 ] [ 回討論區列表 ] [ 回知識入口 ]
2017/8/3 下午 01:20:02
這裡寫錯了,要改一下

> Prototype *D_Obj2 = D_Obj;
> cout << '複製人...\n';
> D_Obj2->Accept(&v);

  Prototype *D_Obj2 = D_Obj->Clone();
  cout << "複製人... \n";
  D_Obj2->Accept(&v);

作者 : ccl0504(手動程式產生器) 貼文超過200則
[ 貼文 448 | 人氣 211 | 評價 910 | 評價/貼文 2.03 | 送出評價 2 次 ] 
[ 給個讚 ]  [ 給個讚 ]  [ 回應本文 ]  [ 發表新文 ]  [ 回上頁 ] [ 回討論區列表 ] [ 回知識入口 ]
2017/8/3 下午 03:15:25

有幫到就好.
有時候可能卡在某個問題點上, 但轉個方向說不定還有其他 solution.


作者 : cxxlman(CxxlMan) C++優秀好手貼文超過1000則
[ 貼文 1003 | 人氣 3227 | 評價 1260 | 評價/貼文 1.26 | 送出評價 27 次 ] 
[ 給個讚 ]  [ 給個讚 ]  [ 回應本文 ]  [ 發表新文 ]  [ 回上頁 ] [ 回討論區列表 ] [ 回知識入口 ]
2017/8/4 下午 06:21:52

修改主要架構如下,修改後可避免 Prototype 直接被𦆻承

template <typename classification>
class Prototype_CRTP;

class Prototype_ROOT;

class Prototype
{
  // Constructor 放這裡為了不被直接繼承
  Prototype() {}
public:
  virtual ~Prototype() {}

  // 永續儲存
  virtual void Save(ostream &save) = 0;
  virtual void Load(istream &load) = 0;

  // 接收訪問者物件,將自己傳給訪問者
  virtual void Accept(Visitor *v) = 0;

  // 取得和實體物件相同的物件
  virtual Prototype *Clone() = 0;
  
  friend class Prototype_ROOT;
};

// Prototype 延伸類別要繼承的最上層類別
class Prototype_ROOT
{
  // 給 class Prototype_CRTP<> 用的
  // 以取得繼承 Prototype 的權力
  class Prototype_R :public Prototype
  {
  public:
    Prototype_R() {}
    ~Prototype_R() {}
  };

public:

  Prototype_ROOT() {}
  ~Prototype_ROOT() {}

  template <typename classification>
  friend class Prototype_CRTP;
};

// 為了可以強制實作及多重繼承
template <typename classification>
class Prototype_CRTP : virtual public Prototype_ROOT::Prototype_R
{
public:
  Prototype_CRTP() {}
  ~Prototype_CRTP(){}
};

#define PROTOTYPE_BASE_CRTP(Base) Base,public Prototype_CRTP<Base>
作者 : cxxlman(CxxlMan) C++優秀好手貼文超過1000則
[ 貼文 1003 | 人氣 3227 | 評價 1260 | 評價/貼文 1.26 | 送出評價 27 次 ] 
[ 給個讚 ]  [ 給個讚 ]  [ 回應本文 ]  [ 發表新文 ]  [ 回上頁 ] [ 回討論區列表 ] [ 回知識入口 ]
2017/8/5 上午 06:52:47

我犯傻了,Prototype_CRTP 要再宣告一次純虛擬函數不能省


// 為了可以強制實作及多重繼承
template <typename classification>
class Prototype_CRTP : virtual public Prototype_ROOT::Prototype_R
{
protected:
  // 永續儲存
  virtual void Save(ostream &save) override = 0;
  virtual void Load(istream &load) override = 0;

  // 接收訪問者物件,將自己傳給訪問者
  virtual void Accept(Visitor *v) override = 0;

  // 取得和實體物件相同的物件
  virtual Prototype *Clone() override = 0;

public:
  Prototype_CRTP() {}
  ~Prototype_CRTP(){}
};
作者 : sunyear(coco) VC++卓越專家C++頂尖高手貼文超過2000則
[ 貼文 2421 | 人氣 1485 | 評價 6060 | 評價/貼文 2.5 | 送出評價 5 次 ] 
[ 給個讚 ]  [ 給個讚 ]  [ 回應本文 ]  [ 發表新文 ]  [ 回上頁 ] [ 回討論區列表 ] [ 回知識入口 ]
2017/11/5 下午 11:32:41
>我說的 must 是為了必須要實作、改寫的虛擬函數而加的,對於不須一定要實作、
>改寫的虛擬函數硬要加上 must 才說不要實作,這是程式員的錯,不要去怪 must
好久沒來這裡了,不應該說好久沒碰程式了。看了一看,發現自己已沒辦法從頭看到尾。看到一半沒體力看下去了,所以若回覆有重覆或己有討論過,就先說聲抱歉。
先說現實世界情況,老爸留下了遺產給兒子,其中包括一輀車。但遺囑吩咐兒子,所有的遺產都可以自由使用處分,唯獨那一輀車子你不能使用和處分。你一定要自行賺錢買
一輛自己的車。
想想看,這合理嗎?
兒子可能的兩種反應:
1.我不能使用和處分,那給我幹嘛,你就把自己裝進車子一起埋到土裡算了。
2.不給我用也就算了,我又不開車,幹嘛逼我一定要買車?

回到C++世界,
(對應)1.若不給子類別使用,就宣告private就好了。
(對應)2.既給繼承就不要預設子類別對繼承的東西使用方式(根本不用,修改或完全改寫)。

對C大舉的clone例子,基類別希望繼承者一定要改寫自己的clone。
如果子類別繼承這個基類別,然後只是加了一個和clone完全無關的函式成員,為什麼要強迫它改寫clone?
我看了看,實在也不是個強迫子類別一定要實作自己的clone的好理由。
作者 : cxxlman(CxxlMan) C++優秀好手貼文超過1000則
[ 貼文 1003 | 人氣 3227 | 評價 1260 | 評價/貼文 1.26 | 送出評價 27 次 ] 
[ 給個讚 ]  [ 給個讚 ]  [ 回應本文 ]  [ 發表新文 ]  [ 回上頁 ] [ 回討論區列表 ] [ 回知識入口 ]
2017/11/7 下午 06:32:44

>對C大舉的clone例子,基類別希望繼承者一定要改寫自己的clone。
>如果子類別繼承這個基類別,然後只是加了一個和clone完全無關的函式成員,為什麼要強迫它改寫clone?
>我看了看,實在也不是個強迫子類別一定要實作自己的clone的好理由。

我只是簡單表達意思,例子不是寫得很完全,我已改用 介面繼承+組合代替繼承 解決了這個問題,參考以下這兩篇

http://www.programmer-club.com.tw/ShowSameTitleN/c/46928.html
http://www.programmer-club.com.tw/ShowSameTitleN/c/46930.html
作者 : sunyear(coco) VC++卓越專家C++頂尖高手貼文超過2000則
[ 貼文 2421 | 人氣 1485 | 評價 6060 | 評價/貼文 2.5 | 送出評價 5 次 ] 
[ 給個讚 ]  [ 給個讚 ]  [ 回應本文 ]  [ 發表新文 ]  [ 回上頁 ] [ 回討論區列表 ] [ 回知識入口 ]
2017/11/7 下午 09:49:30
>我只是簡單表達意思,例子不是寫得很完全,我已改用 介面繼承+組合代替繼承 解決了這個問題,參考以下這兩篇
可不可以做得到並不是我關心的。我比較是想知道這需求的合理性( 或說需要性)。
例如我提的例子,子類別只加了一個跟clone無關的成員函式,為何一定要強制實作另一個clone。
作者 : cxxlman(CxxlMan) C++優秀好手貼文超過1000則
[ 貼文 1003 | 人氣 3227 | 評價 1260 | 評價/貼文 1.26 | 送出評價 27 次 ] 
[ 給個讚 ]  [ 給個讚 ]  [ 回應本文 ]  [ 發表新文 ]  [ 回上頁 ] [ 回討論區列表 ] [ 回知識入口 ]
2017/11/8 下午 02:24:57
應該沒那麼難理解,我再寫個更簡單的例子

#include <iostream>
#include <string>

using namespace std;

class Base
{
public:
 virtual Base *Clone()
 {
  return new Base(*this);
 }
};

class Derived1 :public Base
{
 string m_Name;
public:
 Derived1(const string &Name)
  :m_Name(Name)
 {
 }

 Derived1(const Derived1 &Src)
  :m_Name(Src.m_Name)
 {
 }

 virtual Base *Clone()
 {
  return new Derived1(*this);
 }

 void Show()
 {
  cout << "I am Derived1. My name is " << m_Name << endl;
 }
};

class Derived2 :public Base
{
 string m_Name;
public:
 Derived2(const string &Name)
  :m_Name(Name)
 {
 }

 Derived2(const Derived2 &Src)
  :m_Name(Src.m_Name)
 {
 }

 void Show()
 {
  cout << "I am Derived2. My name is " << m_Name << endl;
 }
};

int main()
{
 Base *pBase1 = new Derived1("coco");
 Base *clone_pBase1 = pBase1->Clone();
 Derived1 *pDerived1 = dynamic_cast<Derived1*>(pBase1);
 pDerived1->Show(); // I am Derived1. My name is coco
 pDerived1 = dynamic_cast<Derived1*>(clone_pBase1);
 pDerived1->Show(); // I am Derived1. My name is coco

 Base *pBase2 = new Derived2("sunyear");
 Base *clone_pBase2 = pBase2->Clone();
 Derived2 *pDerived2 = dynamic_cast<Derived2*>(pBase2);
 pDerived2->Show(); // I am Derived2. My name is sunyear
 pDerived2 = dynamic_cast<Derived2*>(clone_pBase2);
 pDerived2->Show(); // 當掉

 return 0;
}

之所以會出錯是因為 C++ 的虛擬函數只是讓延伸者"可以"覆寫,所以須要一個關鍵字 must,用以表示若有延伸者一定要覆寫
作者 : cxxlman(CxxlMan) C++優秀好手貼文超過1000則
[ 貼文 1003 | 人氣 3227 | 評價 1260 | 評價/貼文 1.26 | 送出評價 27 次 ] 
[ 給個讚 ]  [ 給個讚 ]  [ 回應本文 ]  [ 發表新文 ]  [ 回上頁 ] [ 回討論區列表 ] [ 回知識入口 ]
2017/11/8 下午 02:54:03
以下是改寫成 介面繼承+組合代替繼承 的版本

#include <memory>
#include <iostream>
#include <string>

using namespace std;

class IBase
{
public:
 virtual IBase *Clone() = 0;
};

class Base:public IBase
{
public:
 virtual IBase *Clone() override
 {
  return new Base(*this);
 }
};

class IDerived1 :public IBase
{
public:
 virtual void Show() = 0;
};

class Derived1 :public IDerived1
{
 shared_ptr<IBase> Base_Ptr;
 string m_Name;

public:
 Derived1(const string &Name)
  :Base_Ptr(new Base), // 這裡只是圖個方便,Derived1 不應該認識 Base
  m_Name(Name)
 {
 }

 Derived1(const Derived1 &Src)
  :Base_Ptr(Src.Base_Ptr->Clone()),
  m_Name(Src.m_Name)
 {
 }

 virtual IBase *Clone() override
 {
  return new Derived1(*this);
 }

 void Show() override
 {
  cout << "I am Derived1. My name is " << m_Name << endl;
 }
};

int main()
{
 shared_ptr<IBase> Base1_Ptr(new Derived1("coco"));
 shared_ptr<IBase> clone_Base1_Ptr( Base1_Ptr->Clone());
 shared_ptr<Derived1> Derived1_Ptr = dynamic_pointer_cast<Derived1>(Base1_Ptr);
 Derived1_Ptr->Show(); // I am Derived1. My name is coco
 Derived1_Ptr = dynamic_pointer_cast<Derived1>(clone_Base1_Ptr);
 Derived1_Ptr->Show(); // I am Derived1. My name is coco
 
 return 0;
}

作者 : cheg(cheg)
[ 貼文 99 | 人氣 125 | 評價 410 | 評價/貼文 4.14 | 送出評價 24 次 ] 
[ 給個讚 ]  [ 給個讚 ]  [ 回應本文 ]  [ 發表新文 ]  [ 回上頁 ] [ 回討論區列表 ] [ 回知識入口 ]
2017/11/9 上午 07:41:49

>>我只是簡單表達意思,例子不是寫得很完全,我已改用 介面繼承+組合代替繼承 解決了這個問題,參考以下這兩篇
>可不可以做得到並不是我關心的。我比較是想知道這需求的合理性( 或說需要性)。
>例如我提的例子,子類別只加了一個跟clone無關的成員函式,為何一定要強制實作另一個clone。

這是他要做 prototype 跟 visitor pattern 的緣故
這兩個模式的特性 clone() 跟 accept() 都必須給出 this object 的 static type
因此會發現很多子類別的實作內容一樣,只有這個 this type 會不同
變成不能去復用父類別的實作內容

問題就出在他原本的問題是,在 prototype & visitor 都在第三代的前提下
會因為第二代的實作,失去強制重寫 clone() 跟 accept() 的作用,導致了
程式不預期的錯誤而不自知,然後產生問題:為什麼語言不提供 must ?

然後一開始就在討論說應該改變繼承架構就不會有這問題
現在改介面繼承加組合就是這樣作了,與本來堅持的問題已經無關

作者 : sunyear(coco) VC++卓越專家C++頂尖高手貼文超過2000則
[ 貼文 2421 | 人氣 1485 | 評價 6060 | 評價/貼文 2.5 | 送出評價 5 次 ] 
[ 給個讚 ]  [ 給個讚 ]  [ 回應本文 ]  [ 發表新文 ]  [ 回上頁 ] [ 回討論區列表 ] [ 回知識入口 ]
2017/11/22 上午 01:20:31
>應該沒那麼難理解,我再寫個更簡單的例子
>#include <iostream>
>#include <string>
>
>using namespace std;
>
>class Base
>{
>public:
> virtual Base *Clone()
> {
>  return new Base(*this);
> }
>};
>::::::::::::::::::::::::::::::::::::::::::::
>class Derived2 :public Base
>{
> string m_Name;
>public:
> Derived2(const string &Name)
>  :m_Name(Name)
> {
> }
>
> Derived2(const Derived2 &Src)
>  :m_Name(Src.m_Name)
> {
> }

> void Show()
> {
>  cout << "I am Derived2. My name is " << m_Name << endl;
> }
>};
>
>int main()
>{
>:::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::
> Base *pBase2 = new Derived2("sunyear");
> Base *clone_pBase2 = pBase2->Clone();
> Derived2 *pDerived2 = dynamic_cast<Derived2*>(pBase2);
> pDerived2->Show(); // I am Derived2. My name is sunyear
> pDerived2 = dynamic_cast<Derived2*>(clone_pBase2);
> pDerived2->Show(); // 當掉
>
> return 0;
>}
>
>之所以會出錯是因為 C++ 的虛擬函數只是讓延伸者"可以"覆寫,所以須要一個關鍵字 must,用以表示若有延伸者一定要覆寫

之所以錯是因為Derived2多了一個 m_Name的成員。
撰寫Derived2程式時,本來就應該在clone負責m_Name的複製。誰生的孩子誰就要負責養。
而我前面提出的問題是,若子類別沒增加跟clone 有關的東西,為什麼要被強迫實作?
引用你的Base例子,
class Derived3 :public Base
{
public:
 ShowString(const string &Str)
 {cout <<Str<< endl;}
};
這例子Derived3為什麼 要被強迫實作自己的clone?

再引用你的Derived1為例子
class Derived4 :public Derived1
{
public:
 JustSayHello(const string &Name)
 {cout <<"Hello "<<Name<<'!'<< endl;}
};

這例子Derived4為什麼 要被強迫實作自己的clone?

整理一下我的意思,你的例子Derived1,Derived2該有自己的clone,但Derived3,Derived4則不需要。
要不要有自己的clone,應由程式員自行判斷,自己的孩子自己養,賴給父母當然出問題。但若頂客一族,沒生孩子不用硬抱一個假娃娃來養吧?
作者 : cxxlman(CxxlMan) C++優秀好手貼文超過1000則
[ 貼文 1003 | 人氣 3227 | 評價 1260 | 評價/貼文 1.26 | 送出評價 27 次 ] 
[ 給個讚 ]  [ 給個讚 ]  [ 回應本文 ]  [ 發表新文 ]  [ 回上頁 ] [ 回討論區列表 ] [ 回知識入口 ]
2017/11/22 上午 11:49:47
那樣 dynamic_cast 將會得到 null
作者 : sunyear(coco) VC++卓越專家C++頂尖高手貼文超過2000則
[ 貼文 2421 | 人氣 1485 | 評價 6060 | 評價/貼文 2.5 | 送出評價 5 次 ] 
[ 給個讚 ]  [ 給個讚 ]  [ 回應本文 ]  [ 發表新文 ]  [ 回上頁 ] [ 回討論區列表 ] [ 回知識入口 ]
2017/11/23 上午 01:03:05
>那樣 dynamic_cast 將會得到 null
不懂你的意思。
作者 : cxxlman(CxxlMan) C++優秀好手貼文超過1000則
[ 貼文 1003 | 人氣 3227 | 評價 1260 | 評價/貼文 1.26 | 送出評價 27 次 ] 
[ 給個讚 ]  [ 給個讚 ]  [ 回應本文 ]  [ 發表新文 ]  [ 回上頁 ] [ 回討論區列表 ] [ 回知識入口 ]
2017/11/23 上午 10:00:23
clone 就是要全部複製,若不覆寫就只會 clone 上半部,下半部不見了,自然 dynamic_cast<Derived3> 就會得到 NULL
作者 : sunyear(coco) VC++卓越專家C++頂尖高手貼文超過2000則
[ 貼文 2421 | 人氣 1485 | 評價 6060 | 評價/貼文 2.5 | 送出評價 5 次 ] 
[ 給個讚 ]  [ 給個讚 ]  [ 回應本文 ]  [ 發表新文 ]  [ 回上頁 ] [ 回討論區列表 ] [ 回知識入口 ]
2017/11/27 上午 03:18:02
>clone 就是要全部複製,若不覆寫就只會 clone 上半部,下半部不見了,
>自然 dynamic_cast<Derived3> 就會得到 NULL
那並不是複製不完全所致(複製是完全的),dynamic_cast會得到null是因為向下轉型時型別檢查沒通過。
這種不同階層型別物件操作,應該都使用介面型別IBase的介面去操作。又轉回衍生型別操作本來就俱危險性。
如果有什麼特別理由要轉回衍生型別操作,只要確定是那個衍生型別(你的例子是確定的),可以用static_cast轉型,就可以避免型別檢查。
那若是不確定是那個衍生型別...問題複雜了點...
基本上你的例子,dynamic_cast也是要確定型別才能cast的,不然得到null,下面的程式也是要出問題的。
也就是說,不確定是那個衍生型別的狀況,程式還要去處理得到null時怎麼辦。執行時期 dynamic_cast本就要付一點代價的,
再加上檢查失敗的處理,等於付兩份代價的結果,倒不如在IBase裡多一個WhoAmI()的介面,也是可以動態檢查型別,但只花一份代價。
加上WhoAmI()的型別檢查後再static_cast 就安全了。如心裡還是毛毛的(不信任WhoAmI),當然可以WhoAmI()分派到相應的dynamic_cast再做確認檢查。
只是這就又花了兩份代價了。
總而言之,安全是要付代價的。
作者 : cxxlman(CxxlMan) C++優秀好手貼文超過1000則
[ 貼文 1003 | 人氣 3227 | 評價 1260 | 評價/貼文 1.26 | 送出評價 27 次 ] 
[ 給個讚 ]  [ 給個讚 ]  [ 回應本文 ]  [ 發表新文 ]  [ 回上頁 ] [ 回討論區列表 ] [ 回知識入口 ]
2017/11/27 下午 04:57:16

>>clone 就是要全部複製,若不覆寫就只會 clone 上半部,下半部不見了,
>>自然 dynamic_cast<Derived3> 就會得到 NULL
>那並不是複製不完全所致(複製是完全的),dynamic_cast會得到null是因為向下轉型時型別檢查沒通過。

若複製是完全的,那以下這程式碼的本尊和分身就會得到相同的結果了

Base *pBase2 = new Derived2("sunyear"); // 本尊
Base *clone_pBase2 = pBase2->Clone(); // 分身
Derived2 *pDerived2 = dynamic_cast<Derived2*>(pBase2);
pDerived2->Show(); // I am Derived2. My name is sunyear
pDerived2 = dynamic_cast<Derived2*>(clone_pBase2);
pDerived2->Show(); // 當掉

>這種不同階層型別物件操作,應該都使用介面型別IBase的介面去操作。又轉回衍生型別操作本來就俱危險性。
>如果有什麼特別理由要轉回衍生型別操作,只要確定是那個衍生型別(你的例子是確定的),可以用static_cast轉型,就可以避免型別檢查。

我只是用 dynamic_cast 來證明 clone_pBase2 因 Derived2 沒有複寫 Clone() 會造成複製不完全而已,沒有要這樣使用

作者 : sunyear(coco) VC++卓越專家C++頂尖高手貼文超過2000則
[ 貼文 2421 | 人氣 1485 | 評價 6060 | 評價/貼文 2.5 | 送出評價 5 次 ] 
[ 給個讚 ]  [ 給個讚 ]  [ 回應本文 ]  [ 發表新文 ]  [ 回上頁 ] [ 回討論區列表 ] [ 回知識入口 ]
2017/11/27 下午 05:30:25
>若複製是完全的,那以下這程式碼的本尊和分身就會得到相同的結果了
>Base *pBase2 = new Derived2("sunyear"); // 本尊
>Base *clone_pBase2 = pBase2->Clone(); // 分身
>:::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::
>pDerived2 = dynamic_cast<Derived2*>(clone_pBase2);
>pDerived2->Show(); // 當掉
我說的是Derived3延用基類別的clone的複製是完全的,沒有說是Derived2。
Derived2沒有增加任何的資料成員,延用基類別的clone是可以的;
Derived2有自己新增的資料,當然要自己覆寫clone。
我的意思是子類別依自己的情況決定要不要覆寫clone,不應該被強迫一定要覆寫。像Derived3就不需要覆寫。

>我只是用 dynamic_cast 來證明 clone_pBase2 因 Derived2 沒有複寫 Clone() 會造成複製不完全而已,沒有要這樣使用
不是複製不完全。那是因為沒沒覆寫clone而父類別製造的是Base物件,dynamic_cast會檢查其類別階層而cast失敗。
你的例子,使用static_cast就不會有問題,如因複製不完全,那static_cast就一樣會有問題。
我知道這只是個例子,所以我假設了不確定類別情況的處理。
作者 : sunyear(coco) VC++卓越專家C++頂尖高手貼文超過2000則
[ 貼文 2421 | 人氣 1485 | 評價 6060 | 評價/貼文 2.5 | 送出評價 5 次 ] 
[ 給個讚 ]  [ 給個讚 ]  [ 回應本文 ]  [ 發表新文 ]  [ 回上頁 ] [ 回討論區列表 ] [ 回知識入口 ]
2017/11/27 下午 05:34:15
訂正:
Derived3沒有增加任何的資料成員,延用基類別的clone是可以的;
Derived2有自己新增的資料,當然要自己覆寫clone。
作者 : cxxlman(CxxlMan) C++優秀好手貼文超過1000則
[ 貼文 1003 | 人氣 3227 | 評價 1260 | 評價/貼文 1.26 | 送出評價 27 次 ] 
[ 給個讚 ]  [ 給個讚 ]  [ 回應本文 ]  [ 發表新文 ]  [ 回上頁 ] [ 回討論區列表 ] [ 回知識入口 ]
2017/11/27 下午 07:52:36
若完全複製執行結果應該相同

#include <iostream>
#include <string>

using namespace std;

class Base
{
public:
 virtual Base *Clone()
 {
  return new Base(*this);
 }
};

class Derived3 :public Base
{
public:
 virtual Base *Clone() override 
 {
  return new Derived3(*this);
 }
};

class Derived4 :public Base
{
public:
};

int main()
{
 Base *pBase3 = new Derived3; // 本尊
 Base *clone_pBase3 = pBase3->Clone(); // 分身

 cout << "Derived3 本尊:";
 if (dynamic_cast<Derived3*>(pBase3))
  cout << "完全" << endl;
 else
  cout << "不完全" << endl;

 cout << "Derived3 分身:";
 if (dynamic_cast<Derived3*>(clone_pBase3))
  cout << "完全" << endl;
 else
  cout << "不完全" << endl;

 cout << endl;

 Base *pBase4 = new Derived4; // 本尊
 Base *clone_pBase4 = pBase4->Clone(); // 分身

 cout << "Derived4 本尊:";
 if (dynamic_cast<Derived4*>(pBase4))
  cout << "完全" << endl;
 else
  cout << "不完全" << endl;

 cout << "Derived4 分身:";
 if (dynamic_cast<Derived4*>(clone_pBase4))
  cout << "完全" << endl;
 else
  cout << "不完全" << endl;

 return 0;
}

執行結果:
Derived3 本尊:完全
Derived3 分身:完全

Derived4 本尊:完全
Derived4 分身:不完全
請按任意鍵繼續 . . .
作者 : sunyear(coco) VC++卓越專家C++頂尖高手貼文超過2000則
[ 貼文 2421 | 人氣 1485 | 評價 6060 | 評價/貼文 2.5 | 送出評價 5 次 ] 
[ 給個讚 ]  [ 給個讚 ]  [ 回應本文 ]  [ 發表新文 ]  [ 回上頁 ] [ 回討論區列表 ] [ 回知識入口 ]
2017/11/27 下午 09:14:13
> if (dynamic_cast<Derived4*>(clone_pBase4))
>  cout << "完全" << endl;
> else
>  cout << "不完全" << endl;
你沒有明白我的意思。
dynamic_cast是一個會動態判斷是否安全向下轉型(Safe downcasting)的casting不是判斷複製完不完全。
對於Derived4,因是使用基類別來複製自己,所己物件原本型態會是基類別,在dynamic_cast當然會被判斷非法。
(對dynamic_cast來說,只有衍伸類別降轉基類別的物件再轉回衍伸類別才是安全的)
但事實上Derived4物件和其基類別資料沒什麼不同(內容物完全相同),互轉是不價有問題的,程式員只要clone_pBase4身份是確定的。
那麼 Derived4 *pClone4= static_cast<Derived4*>(clone_pBase4);
然後pClone4完全可以當作 Derived4的物件來用,不會有複製完不完全的問題。
作者 : sunyear(coco) VC++卓越專家C++頂尖高手貼文超過2000則
[ 貼文 2421 | 人氣 1485 | 評價 6060 | 評價/貼文 2.5 | 送出評價 5 次 ] 
[ 給個讚 ]  [ 給個讚 ]  [ 回應本文 ]  [ 發表新文 ]  [ 回上頁 ] [ 回討論區列表 ] [ 回知識入口 ]
2017/11/27 下午 10:27:01
好吧,我改寫一下程式,或許較能澄清
if (dynamic_cast<Derived4*>(clone_pBase4))
 cout << "安全轉型" << endl;
else
 cout << "不安全轉型" << endl;

注意: "(不)安全" 不是 "(不)完全"
所謂"不安全"只是沒辦法保證安全,並不是說轉型一定是錯的。
如果程式員確定轉型是對的,可以用static_cast來強制轉型。
作者 : cxxlman(CxxlMan) C++優秀好手貼文超過1000則
[ 貼文 1003 | 人氣 3227 | 評價 1260 | 評價/貼文 1.26 | 送出評價 27 次 ] 
[ 給個讚 ]  [ 給個讚 ]  [ 回應本文 ]  [ 發表新文 ]  [ 回上頁 ] [ 回討論區列表 ] [ 回知識入口 ]
2017/11/28 下午 06:27:17

>好吧,我改寫一下程式,或許較能澄清
>if (dynamic_cast<Derived4*>(clone_pBase4))
> cout << '安全轉型' << endl;
>else
> cout << '不安全轉型' << endl;
>
>注意: '(不)安全' 不是 '(不)完全'
>所謂'不安全'只是沒辦法保證安全,並不是說轉型一定是錯的。
只有完全或不完全,沒有不安全這種事,
檢驗不通過就是 Blog() 沒覆寫或是寫錯了

>如果程式員確定轉型是對的,可以用static_cast來強制轉型。
由 Base 轉到 Derived 嗎,要是多重繼承不一定會成功
作者 : sunyear(coco) VC++卓越專家C++頂尖高手貼文超過2000則
[ 貼文 2421 | 人氣 1485 | 評價 6060 | 評價/貼文 2.5 | 送出評價 5 次 ] 
[ 給個讚 ]  [ 給個讚 ]  [ 回應本文 ]  [ 發表新文 ]  [ 回上頁 ] [ 回討論區列表 ] [ 回知識入口 ]
2017/11/28 下午 06:54:01
>只有完全或不完全,沒有不安全這種事,
>檢驗不通過就是 Blog() 沒覆寫或是寫錯了
所以static_cast應該是C++的一個bug :D
作者 : cxxlman(CxxlMan) C++優秀好手貼文超過1000則
[ 貼文 1003 | 人氣 3227 | 評價 1260 | 評價/貼文 1.26 | 送出評價 27 次 ] 
[ 給個讚 ]  [ 給個讚 ]  [ 回應本文 ]  [ 發表新文 ]  [ 回上頁 ] [ 回討論區列表 ] [ 回知識入口 ]
2017/11/28 下午 09:04:59


>所以static_cast應該是C++的一個bug :D
static_cast 依賴的是編譯器的能力,只要不用惡搞手法,能過得了應該沒問題
作者 : sunyear(coco) VC++卓越專家C++頂尖高手貼文超過2000則
[ 貼文 2421 | 人氣 1485 | 評價 6060 | 評價/貼文 2.5 | 送出評價 5 次 ] 
[ 給個讚 ]  [ 給個讚 ]  [ 回應本文 ]  [ 發表新文 ]  [ 回上頁 ] [ 回討論區列表 ] [ 回知識入口 ]
2017/11/29 上午 12:09:51
>static_cast 依賴的是編譯器的能力,只要不用惡搞手法,能過得了應該沒問題
dynamic_cast 才是依賴編譯器的能力吧?
它是依賴RTTI 的type information檢查downcast的安全性。
沒什麼不好(雖然有人認為,好的C++程式不應該用dynamic_cast ),但有明明可以轉的東西,它會轉不動。
這時就是static_cast上場,static_cast的隱含意義就是,我知道這轉型OK沒問題,不要幫我檢查,結果我自己負責。
有些東西會是這種情況(像是通訊介面程式),dynamic_cast 過不了,只有用static_cast才可以。
static_cast<Derived4*>(clone_pBase4)可以轉型(因為是一樣的資料成員),使用也沒有問題。如果一定堅持dynamic_cast才是對的,那static_cast就無用武之地了。

作者 : cxxlman(CxxlMan) C++優秀好手貼文超過1000則
[ 貼文 1003 | 人氣 3227 | 評價 1260 | 評價/貼文 1.26 | 送出評價 27 次 ] 
[ 給個讚 ]  [ 給個讚 ]  [ 回應本文 ]  [ 發表新文 ]  [ 回上頁 ] [ 回討論區列表 ] [ 回知識入口 ]
2017/11/29 下午 09:16:20

>>static_cast 依賴的是編譯器的能力,只要不用惡搞手法,能過得了應該沒問題
>dynamic_cast 才是依賴編譯器的能力吧?

我意思是說 static_cast 是在編譯時期由編譯器就轉換好的,dynamic_cast 是在執行時期由 RTTI 查找出來的

>它是依賴RTTI 的type information檢查downcast的安全性。
>沒什麼不好(雖然有人認為,好的C++程式不應該用dynamic_cast ),但有明明可以轉的東西,它會轉不動。
>這時就是static_cast上場,static_cast的隱含意義就是,我知道這轉型OK沒問題,不要幫我檢查,結果我自己負責。
>有些東西會是這種情況(像是通訊介面程式),dynamic_cast 過不了,只有用static_cast才可以。

有這種事? 據我所知只要能順利建好 RTTI 就一定能轉,若不能轉就是不應該這樣轉

>static_cast<Derived4*>(clone_pBase4)可以轉型(因為是一樣的資料成員),使用也沒有問題。如果一定堅持dynamic_cast才是對的,那static_cast就無用武之地了。

我認為 static_cast 不應該 downcast,這編譯器應能擋掉,不知是什麼原因不這樣做
作者 : sunyear(coco) VC++卓越專家C++頂尖高手貼文超過2000則
[ 貼文 2421 | 人氣 1485 | 評價 6060 | 評價/貼文 2.5 | 送出評價 5 次 ] 
[ 給個讚 ]  [ 給個讚 ]  [ 回應本文 ]  [ 發表新文 ]  [ 回上頁 ] [ 回討論區列表 ] [ 回知識入口 ]
2017/11/29 下午 10:18:59
>有這種事? 據我所知只要能順利建好 RTTI 就一定能轉,若不能轉就是不應該這樣轉
如果dynamic_cast可以決定能不能轉,不能轉就是錯的。那就dynamic_cast決定一切就好了,何必有static_cast?

>有這種事? 據我所知只要能順利建好 RTTI 就一定能轉,若不能轉就是不應該這樣轉
物件若都是在你同一個程式下生成,當然覺得不應該有這等事。
若兩個程式(可能在同一個系統也可能在不同的裝置下),傳過來的物件如何通過dynamic_cast?
這通常是靠通訊協定確定資料結構,然後就強轉。
總不能說因不能通過dynamic_cast,然後說C++不適合撰寫通訊協定吧。
再澄清一次,dynamic_cast不能通過,只是檢測不能確認安全,不是一定是個錯誤。
前面我那個Derived3的資料結構和Base是一模一樣的,只要確定這個,強轉就沒問題。
當然以C++的觀點,在同一個程式內產生的物件,做到要強轉不是好事。但同樣dynamic_cast也不是好事。
有此一派說法,C++的程式不應該用到dynamic_cast。所有用到dynamic_cast都可以用別的較好的方式解決。
這個說法的立論之一,是安全是程式員責任不是編譯器,不是編譯器;其二是是好的程式是不需要Downcast的。
但沒聽過那派說法,說static_cast是不需要的強轉。(但這不是說,把所有的dynamic_cast改成static_cast的意思喔)
這和以前有關goto該不該用的爭議有點像,我比較同意不該濫用而不是絕對不能用。
作者 : cxxlman(CxxlMan) C++優秀好手貼文超過1000則
[ 貼文 1003 | 人氣 3227 | 評價 1260 | 評價/貼文 1.26 | 送出評價 27 次 ] 
[ 給個讚 ]  [ 給個讚 ]  [ 回應本文 ]  [ 發表新文 ]  [ 回上頁 ] [ 回討論區列表 ] [ 回知識入口 ]
2017/12/1 下午 03:15:57

>>有這種事? 據我所知只要能順利建好 RTTI 就一定能轉,若不能轉就是不應該這樣轉
>如果dynamic_cast可以決定能不能轉,不能轉就是錯的。那就dynamic_cast決定一切就好了,何必有static_cast?

基本上是的,但在速度上差很大,而且 dynamic_cast 不會強制轉形

>>有這種事? 據我所知只要能順利建好 RTTI 就一定能轉,若不能轉就是不應該這樣轉
>物件若都是在你同一個程式下生成,當然覺得不應該有這等事。
>若兩個程式(可能在同一個系統也可能在不同的裝置下),傳過來的物件如何通過dynamic_cast?
>這通常是靠通訊協定確定資料結構,然後就強轉。

你想太多了,dynamic_cast 只做同一個物件介面間的轉換,不是物件之間的轉換
作者 : sunyear(coco) VC++卓越專家C++頂尖高手貼文超過2000則
[ 貼文 2421 | 人氣 1485 | 評價 6060 | 評價/貼文 2.5 | 送出評價 5 次 ] 
[ 給個讚 ]  [ 給個讚 ]  [ 回應本文 ]  [ 發表新文 ]  [ 回上頁 ] [ 回討論區列表 ] [ 回知識入口 ]
2017/12/1 下午 06:17:04
>你想太多了,dynamic_cast 只做同一個物件介面間的轉換,不是物件之間的轉換
不是想太多,只是指出,有些轉型是對的也是必需,但dynamic_cast無法發揮作用。
不只是同一個程式內型別的轉換,dynamic_cast判別的依據是存放在虛擬函式表裡面。
若是類別沒有虛擬函式,dynamic_cast也無法發揮作用。

總之,可不可以轉型,還是以程式員判斷為主,dynamic_cast只是幫點忙。
說dynamic_cast可以轉型成功,那轉型一定是安全的,是對的。
但若說dynamic_cast轉型不成功,那轉型一定是錯的,那就對dynamic_cast依賴過深了。

作者 : cxxlman(CxxlMan) C++優秀好手貼文超過1000則
[ 貼文 1003 | 人氣 3227 | 評價 1260 | 評價/貼文 1.26 | 送出評價 27 次 ] 
[ 給個讚 ]  [ 給個讚 ]  [ 回應本文 ]  [ 發表新文 ]  [ 回上頁 ] [ 回討論區列表 ] [ 回知識入口 ]
2017/12/2 下午 09:05:18
我是搞不出你說的狀況,你弄一個來看看,在此之前我認為 dynamic_cast 絕對正確,否則 RTTI 的價值將一夕
之間瓦解,RTTI 本來就是做 class type 的關係判斷用的,必須要絕對可靠

#include <iostream>

using namespace std;

class A1
{
public:
 virtual ~A1() {}
};

class A2
{
};

class B1:public A1, public A2
{
};

class B2 :public A1
{
};

class C1 :public B1
{
};

class C2 :public B2
{
};

int main()
{
 A1 *pA1 = new C1;
 
 A2 *pA2 = dynamic_cast<A2*>(pA1);
 cout << "dynamic_cast<A2*>(pA1) pA1 is C1: " << ((pA2) ? "YES" : "NO") << endl; // YRS

// C1 *pC1 = dynamic_cast<C1*>(pA2); // A2 沒有 RTTI 編譯自動擋掉
// cout << "dynamic_cast<C1*>(pA2) pA2 is C1: " << ((pC1) ? "YES" : "NO") << endl;

 pA1 = new C2;

 pA2 = dynamic_cast<A2*>(pA1);
 cout << "dynamic_cast<A2*>(pA1) pA1 is C2: " << ((pA2) ? "YES" : "NO") << endl; // NO

// C1 *pC1 = dynamic_cast<C1*>(pA2); // A2 沒有 RTTI 編譯自動擋掉
// cout << "dynamic_cast<C1*>(pA2) pA2 is C2: " << ((pC1) ? "YES" : "NO") << endl;

 return 0;
}
作者 : sunyear(coco) VC++卓越專家C++頂尖高手貼文超過2000則
[ 貼文 2421 | 人氣 1485 | 評價 6060 | 評價/貼文 2.5 | 送出評價 5 次 ] 
[ 給個讚 ]  [ 給個讚 ]  [ 回應本文 ]  [ 發表新文 ]  [ 回上頁 ] [ 回討論區列表 ] [ 回知識入口 ]
2017/12/3 下午 09:23:48
>我是搞不出你說的狀況,你弄一個來看看
你是指這句話:"若是類別沒有虛擬函式,dynamic_cast也無法發揮作用"?
你的例子,編譯器不是己經發出,因沒虛擬函式無法使用dynamic_cast了嗎?
至於有虛擬函式的情況,我前面Derived3的例子就是啊。
它用static_cast是可工作的,執行也沒問題( 就你把類別弄得更複雜,執行上不會有錯)。
但你要堅持不能使用dynamic_cast就是錯的,那我也沒辦法了。
所有正式的文件都說dynamic_cast檢查不過是不安全的,但都沒說檢查不過就是錯的。
老師跟學生說,過馬路沒道路導護指揮交通是不安全的。在沒道路導護下,學生可以選擇不過馬路或自行注意安全。
學生左右看確實沒來車了,過馬路是安全的。學生可以選擇自行負責安全。
作者 : cxxlman(CxxlMan) C++優秀好手貼文超過1000則
[ 貼文 1003 | 人氣 3227 | 評價 1260 | 評價/貼文 1.26 | 送出評價 27 次 ] 
[ 給個讚 ]  [ 給個讚 ]  [ 回應本文 ]  [ 發表新文 ]  [ 回上頁 ] [ 回討論區列表 ] [ 回知識入口 ]
2017/12/6 上午 08:54:40

>>我是搞不出你說的狀況,你弄一個來看看
>你是指這句話:'若是類別沒有虛擬函式,dynamic_cast也無法發揮作用'?

是說這句 "所謂'不安全'只是沒辦法保證安全,並不是說轉型一定是錯的" 這句 "dynamic_cast不能通過,只是檢測不能確認安全,不是一定是個錯誤" 還有這句 "但你要堅持不能使用dynamic_cast就是錯的,那我也沒辦法了"

>你的例子,編譯器不是己經發出,因沒虛擬函式無法使用dynamic_cast了嗎?
>至於有虛擬函式的情況,我前面Derived3的例子就是啊。
>它用static_cast是可工作的,執行也沒問題( 就你把類別弄得更複雜,執行上不會有錯)。
>但你要堅持不能使用dynamic_cast就是錯的,那我也沒辦法了。
>所有正式的文件都說dynamic_cast檢查不過是不安全的,但都沒說檢查不過就是錯的。
>老師跟學生說,過馬路沒道路導護指揮交通是不安全的。在沒道路導護下,學生可以選擇不過馬路或自行注意安全。
>學生左右看確實沒來車了,過馬路是安全的。學生可以選擇自行負責安全。

我說 dynamic_cast<I*>(pObj) 轉不過就是錯是指 I 這層介面不存在於 pObj 物件之中,而你的不安全是指就算轉不過,I 還是有可能存在 pObj 中是嗎???若是的話就和我的認知相差太多了,請你說清楚,最好能寫出實際例子
作者 : sunyear(coco) VC++卓越專家C++頂尖高手貼文超過2000則
[ 貼文 2421 | 人氣 1485 | 評價 6060 | 評價/貼文 2.5 | 送出評價 5 次 ] 
[ 給個讚 ]  [ 給個讚 ]  [ 回應本文 ]  [ 發表新文 ]  [ 回上頁 ] [ 回討論區列表 ] [ 回知識入口 ]
2017/12/6 下午 06:05:18
>我說 dynamic_cast<I*>(pObj) 轉不過就是錯是指 I 這層介面不存在於 pObj 物件之中,
>而你的不安全是指就算轉不過,I 還是有可能存在 pObj 中是嗎???
>若是的話就和我的認知相差太多了,請你說清楚,最好能寫出實際例子
我的例子在2017/11/22 上午 01:20:31就是。
不知你的"介面"和"存在"指的是什麼,我只能解釋我的認知:
所謂類別物件,分成"資料"(含虛擬函式表)和函式。
我們在傳傳遞或複製物件時,事實上只是在傳遞或複製"資料"。函式只靠型別取用,並不需要真正傳遞或複製。
狹義的說,所謂物件指的只是"資料"並不含函式本體。
所以確認資料成員(或說資料結構)相同的類別,是可以互轉的,即使在dynamic_cast無法成功轉型。
以document view的解釋,一個document可以好幾個不同的view。
document就是物件資料,view就是類別函式。不同的view可以以不同的形式顯示同一個物件資料(本文,圖形,聲音...)
下面是一個例子,一個座標物件,可以有多種(類別)形式的表達:
class Coordinate
{
protected:
double m_X;
double m_Y;
public:
Coordinate(double x, double y) { {m_X = x, m_Y = y; } }
virtual ~Coordinate() {}
};

class CPoint : public Coordinate
{
public:
void ShowPoint() { cout << "Point at (" << m_X <<','<< m_Y<<')' << endl; }
};

class CTan : public Coordinate
{
public:
void ShowTangent() {
cout << "Tangent is " << (m_Y / m_X) << endl;}
};

class CDrawPoint : public CPoint
{
public:
void DrawPoint() {
ShowPoint();
//Draw the point...
}
};


int main()
{
Coordinate Pt(12.0, 22.8);
CPoint *pPoint = static_cast<CPoint *>(& Pt);
pPoint->ShowPoint();
CTan *pTangent = static_cast< CTan *>(&Pt);
pTangent->ShowTangent();
CDrawPoint *pDrawPoint = static_cast< CDrawPoint *>(&Pt);
pDrawPoint->DrawPoint();
return 0;
}
以上幾個物件是可以轉來轉去,以操作同一個物件的(想像document-view的觀今念,一個資料有不同形式的操作)。
但沒有一個轉型可以使用dynamic_cast。


作者 : cxxlman(CxxlMan) C++優秀好手貼文超過1000則
[ 貼文 1003 | 人氣 3227 | 評價 1260 | 評價/貼文 1.26 | 送出評價 27 次 ] 
[ 給個讚 ]  [ 給個讚 ]  [ 回應本文 ]  [ 發表新文 ]  [ 回上頁 ] [ 回討論區列表 ] [ 回知識入口 ]
2017/12/7 上午 10:02:58
你在開什麼玩笑,你寫的例子不是就在說 static_cast 很不安全,要用 dynamic_cast 才安全,你怎麼反過來說

作者 : sunyear(coco) VC++卓越專家C++頂尖高手貼文超過2000則
[ 貼文 2421 | 人氣 1485 | 評價 6060 | 評價/貼文 2.5 | 送出評價 5 次 ] 
[ 給個讚 ]  [ 給個讚 ]  [ 回應本文 ]  [ 發表新文 ]  [ 回上頁 ] [ 回討論區列表 ] [ 回知識入口 ]
2017/12/7 下午 12:40:44
>你在開什麼玩笑,你寫的例子不是就在說 static_cast 很不安全,要用 dynamic_cast 才安全,你怎麼反過來說
怎樣的不安全?寫個例子證明我的例子會出錯。不要又用dynamic_cast 會return null來說它是錯的。
使用會錯才叫錯,檢測說不知是否安全不能叫錯。
我的例子是在說明:
確認資料成員(或說資料結構)相同的類別,是可以互轉的,即使在dynamic_cast無法成功轉型。
那幾個互轉的類別,資料結構完全相同,互有什麼問題?就因dynamic_cast無法成功?
可否找個有說說服力的文件有說,dynamic_cast無法成功轉型的物件就一定不能轉?(會出錯)
如果只有dynamic_cast成功才可以轉型,就跟本不應該有static_cast這東西了。
作者 : cxxlman(CxxlMan) C++優秀好手貼文超過1000則
[ 貼文 1003 | 人氣 3227 | 評價 1260 | 評價/貼文 1.26 | 送出評價 27 次 ] 
[ 給個讚 ]  [ 給個讚 ]  [ 回應本文 ]  [ 發表新文 ]  [ 回上頁 ] [ 回討論區列表 ] [ 回知識入口 ]
2017/12/7 下午 03:51:20
static_cast 某種程度上可以做到強制轉形,強制轉形還拿出來比較安不安全...:(

#include <iostream>

using namespace std;

class Coordinate
{
protected:
 double m_X;
 double m_Y;
public:
 Coordinate(double x, double y) { {m_X = x, m_Y = y; } }
 virtual ~Coordinate() {}
};

class A :public Coordinate
{
 int i;
public:
 A()
  :Coordinate(0,0)
 {
 }

 void Set(double x, double y)
 {
  i = x*y;
  m_X = x;
  m_Y = y;
 }
};

class B :public Coordinate
{
 float i;
public:
 B(double x, double y)
  :Coordinate(x, y)
 {
  i = x*y;
 }
 void Show()
 {
  cout << m_X << " x " << m_Y << " = " << i << endl;
 }
};

int main()
{
 B B_Obj(2, 2);
 Coordinate *pCoordinate = &B_Obj;

 A *pA = static_cast<A*>(pCoordinate); // 惡搞式強制轉形
 pA->Set(10, 10);

 B_Obj.Show();

 return 0;
}

作者 : sunyear(coco) VC++卓越專家C++頂尖高手貼文超過2000則
[ 貼文 2421 | 人氣 1485 | 評價 6060 | 評價/貼文 2.5 | 送出評價 5 次 ] 
[ 給個讚 ]  [ 給個讚 ]  [ 回應本文 ]  [ 發表新文 ]  [ 回上頁 ] [ 回討論區列表 ] [ 回知識入口 ]
2017/12/8 上午 12:09:12
>static_cast 某種程度上可以做到強制轉形,強制轉形還拿出來比較安不安全...:(
前面不是一直強調,程式員確定物件的資料結構是一樣的,就可以強制轉型。
document view的觀念也是相同的資料(結構),不同操作的觀念。
你故意做成不同的資料(結構)去強制轉型,然後證明static_cast不安全,還是不能用?
程式安不安全不是靠dynamic_cast啦,就算你堅持不能使用static_cast(除非dynamic_cast可以過關)。
程式員不能對自己負責,就算一生一世堅持不使用static_cast,還是會漏洞一大堆。
若不能負責自己安全,最好dynamic_cast也都不要用,downcast本來就不是必要的(基本上都有更好的寫法替代)。
作者 : cxxlman(CxxlMan) C++優秀好手貼文超過1000則
[ 貼文 1003 | 人氣 3227 | 評價 1260 | 評價/貼文 1.26 | 送出評價 27 次 ] 
[ 給個讚 ]  [ 給個讚 ]  [ 回應本文 ]  [ 發表新文 ]  [ 回上頁 ] [ 回討論區列表 ] [ 回知識入口 ]
2017/12/8 下午 01:14:22

>>static_cast 某種程度上可以做到強制轉形,強制轉形還拿出來比較安不安全...:(
>前面不是一直強調,程式員確定物件的資料結構是一樣的,就可以強制轉型。
>document view的觀念也是相同的資料(結構),不同操作的觀念。

不管有沒有一樣,強制轉型都有可能幫你轉

>你故意做成不同的資料(結構)去強制轉型,然後證明static_cast不安全,還是不能用?
>程式安不安全不是靠dynamic_cast啦,就算你堅持不能使用static_cast(除非dynamic_cast可以過關)。
>程式員不能對自己負責,就算一生一世堅持不使用static_cast,還是會漏洞一大堆。

static_cast 什麼時候觸及到了安全這領域了,它只是為了達到目的的手段,你就爽爽用就好,只要控管得宜還是能完成任務

>若不能負責自己安全,最好dynamic_cast也都不要用,downcast本來就不是必要的(基本上都有更好的寫法替代)。

我只是確定用 dynamic_cast 做 downcast 有絕對的可信度,所以用它作為以上例子的檢測使用,我也很少用 dynamic_cast,主因是它太慢了,所以真正用於程式的都是其它方法來實現


作者 : sunyear(coco) VC++卓越專家C++頂尖高手貼文超過2000則
[ 貼文 2421 | 人氣 1485 | 評價 6060 | 評價/貼文 2.5 | 送出評價 5 次 ] 
[ 給個讚 ]  [ 給個讚 ]  [ 回應本文 ]  [ 發表新文 ]  [ 回上頁 ] [ 回討論區列表 ] [ 回知識入口 ]
2017/12/8 下午 01:50:12
>不管有沒有一樣,強制轉型都有可能幫你轉
所以程式員確認可以轉就可以了。尤其是dynamic_cast無法轉但不是錯誤的時候。

>>你故意做成不同的資料(結構)去強制轉型,然後證明static_cast不安全,還是不能用?
>>程式安不安全不是靠dynamic_cast啦,就算你堅持不能使用static_cast(除非dynamic_cast可以過關)。
>>程式員不能對自己負責,就算一生一世堅持不使用static_cast,還是會漏洞一大堆。
>static_cast 什麼時候觸及到了安全這領域了,它只是為了達到目的的手段,你就爽爽用就好,只要控管得宜還是能完成任務
那一句話讓你解讀成static_cast候觸及到安全領域?
我都講了多少次,程式員自行負責安全,就可用static_cast,不就表明static_cast 不會在安全上有什麼幫忙。程式員要自己負責安全。

>我只是確定用 dynamic_cast 做 downcast 有絕對的可信度,所以用它作為以上例子的檢測使用,
它的可信度是它確認可以轉,基本上是安全的,但不能轉不表示是錯誤的。
不能轉,只是rtti沒有正確的資訊。

>我也很少用 dynamic_cast,主因是它太慢了,所以真正用於程式的都是其它方法來實現
不只是慢的問題,從繼承觀念來說downcast就不是一個好程式的寫法。
你的clone例子和我coordinate的例子,都有更合乎C++精神的寫法,不需要downcast。
static_cast則是在某些場合會需要的。例如前面提到的通訊。
作者 : cxxlman(CxxlMan) C++優秀好手貼文超過1000則
[ 貼文 1003 | 人氣 3227 | 評價 1260 | 評價/貼文 1.26 | 送出評價 27 次 ] 
[ 給個讚 ]  [ 給個讚 ]  [ 回應本文 ]  [ 發表新文 ]  [ 回上頁 ] [ 回討論區列表 ] [ 回知識入口 ]
2017/12/8 下午 04:49:54

我想在轉型這點上要先講清楚,dynamic_cast 是在物件有包含的 class 之間的轉型,RTTI 負責做好這些記錄,dynamic_cast 就是在 RTTI 的記錄中查找,有找到就轉到要求的 class 這層介面上,沒找到就表示物件不包含這個 class 的成份,會回覆 NULL,所以 dynamic_cast 保證正確與安全

反觀強制轉型就是把某個資料結構要當作哪種樣貌來看的意思,所以 int intVar = (int)floatVar 只要編譯器同意你這樣轉,你就可以當作 int 來處理,這只是一種手段,和 oo 觀念中的轉型是不一樣,不要因為都叫轉型,就在那裡魚目混珠


作者 : sunyear(coco) VC++卓越專家C++頂尖高手貼文超過2000則
[ 貼文 2421 | 人氣 1485 | 評價 6060 | 評價/貼文 2.5 | 送出評價 5 次 ] 
[ 給個讚 ]  [ 給個讚 ]  [ 回應本文 ]  [ 發表新文 ]  [ 回上頁 ] [ 回討論區列表 ] [ 回知識入口 ]
2017/12/8 下午 08:55:18
>我想在轉型這點上要先講清楚,dynamic_cast 是在物件有包含的 class 之間的轉型,RTTI 負責做好這些記錄...
這個你知我知大家知,不需要討論。
重點在,
若沒RTTI記錄,可不可以轉?怎麼轉?
若RTTI記錄不能轉,實際上可以轉,轉了不會有問題,可不可以轉?/

>所以 dynamic_cast 保證正確與安全
dynamic_cast不保證安全的轉型,程式員可不可以自行負責和決定要不要轉?

>反觀強制轉型就是把某個資料結構要當作哪種樣貌來看的意思,所以 int intVar = (int)floatVar 只要編譯器同意你這樣轉,你就可以當作 int 來處理,
不是,強制轉型意思是對錯和後果自負。

>這只是一種手段,和 oo 觀念中的轉型是不一樣,不要因為都叫轉型,就在那裡魚目混珠
不知"魚目混珠"罪名從何而來?
以前跟你討論,雖有爭執,但都還在就事論事的討論。突然安個罪名,是不是該停止討論了?
C++的轉型是從C的轉型的改良版。雖然有的轉型的規則涉及到繼承關係,但不需要搞到00觀念的轉型這麼偉大吧。
(C也是有轉型規則,只是少了繼承開關係的規則)

簡化爭執點,
我那一個coordinate的程式,什麼地方有錯?(不是討論好不好喔)
若是沒有錯,我們爭執什麼?
若是有錯,請指出那裡錯。
你若要說因為dynamic_cast不能通過。
那程式隨你改,限制條件只有Coordinate的子類別不能增加資料成員(包括虛擬函式成員)。
若程式任你怎麼改,都不能使程式因為static_cast而出錯,那為什麼說它錯,而不是dynamic_cast有所不能?

前面提過的觀念,你沒正面回應,它其實是關鍵:
一個類別分為資料成員的操作成員。
如果兩個類別資料成員相同的,那這兩個類別的物件是可以互轉的。
你要說它錯,要能指出那裡錯,而不能只是說dynamic_cast不通過。
dynamic_cast本來就有它的限制,沒虛擬函式不能運作,(目前)只能靠RTTI的type information來檢視,沒有辨識資料成員結構是否相同。
所以dynamic_cast檢查不過,就只代表從type information看不出可以轉型,就這樣而己,不必把dynamic_cast提升到對錯標準。

作者 : cxxlman(CxxlMan) C++優秀好手貼文超過1000則
[ 貼文 1003 | 人氣 3227 | 評價 1260 | 評價/貼文 1.26 | 送出評價 27 次 ] 
[ 給個讚 ]  [ 給個讚 ]  [ 回應本文 ]  [ 發表新文 ]  [ 回上頁 ] [ 回討論區列表 ] [ 回知識入口 ]
2017/12/9 上午 11:52:01
RTTI 和強製轉型是兩個不同領域應用的東西,不能混為一談,若把強製轉型改名叫變型可能在字面和概念上會比較清楚,
變型是是比較低階語言的處理手法,就像組合語言把一塊記憶體當作哪種 type 來看待的意義相當

RTTI 是要用於 oo 這領域的東西,你說 RTTI 須有 vTable 才會存在,這也沒辦法,C++ 就這樣設計,也只好採用有 vTable 的結構,
這是不同領域的問題,不是 C++ 技術的問題,轉型和變型無法互相取代
作者 : sunyear(coco) VC++卓越專家C++頂尖高手貼文超過2000則
[ 貼文 2421 | 人氣 1485 | 評價 6060 | 評價/貼文 2.5 | 送出評價 5 次 ] 
[ 給個讚 ]  [ 給個讚 ]  [ 回應本文 ]  [ 發表新文 ]  [ 回上頁 ] [ 回討論區列表 ] [ 回知識入口 ]
2017/12/9 下午 03:28:07
>RTTI 和強製轉型是兩個不同領域應用的東西,不能混為一談,若把強製轉型改名叫變型可能在字面和概念上會比較清楚,
混為一談還是跟說我魚目混珠意思差不多。只是扣一個這樣的帽子,好歹引個我那裡魚目混珠的論述吧?
從來沒說dynamic_cast和static_cast是一樣的,爭議點都在我使用static_cast的地方,你用因為dynamic_cast不會通過,所以是錯的吧。
(你把該用static_cast的地方,堅持要用dynamic_cast轉型才有混為一談的嫌疑吧)
你把dynamic_cast做做對錯的標準,我認為它不通過只表明不安全,並不表示程式是錯的。程式員認為是對的,就可以用static_cast轉型,你要叫變型,叫魔法都可以。
我可以把前面所有的貼文static_cast全部改為變型,論述不變,爭議不變,有什麼意義?
爭議是我認為那coordinate可以運作無誤的。你要說它錯,要指出何狀況不能運作,而不是以dynamic_cast不能過,所以不能這樣寫。

另外要談到名詞對錯,把static_cast稱入強制轉型,才真的是錯的。
C++的強制轉型是reinterpret_cast
static_cast正確叫靜態轉型,它也是有檢查功能的,只是檢查限於編譯階段(轉換還是在運行階段)。
dynamic_cast相對於編譯階段的檢查,它在有type information 的條件下可以在運行階段做安全性檢查

這些相信你都知道,請相信我也清楚。不要老是繞到好像我不了這幾個cast的功能。
我那coordinate的程式,基礎就是相同的資料結構,不同的操作,使用繼承和static_cast實現。
我前一篇覆文,己簡化問題點,請就我的問題回覆,不要又轉到名詞當與不當。

作者 : cxxlman(CxxlMan) C++優秀好手貼文超過1000則
[ 貼文 1003 | 人氣 3227 | 評價 1260 | 評價/貼文 1.26 | 送出評價 27 次 ] 
[ 給個讚 ]  [ 給個讚 ]  [ 回應本文 ]  [ 發表新文 ]  [ 回上頁 ] [ 回討論區列表 ] [ 回知識入口 ]
2017/12/9 下午 05:40:26

>混為一談還是跟說我魚目混珠意思差不多。只是扣一個這樣的帽子,好歹引個我那裡魚目混珠的論述吧?

class Coordinate
{
protected:
 double m_X;
 double m_Y;
public:
 Coordinate(double x = 0, double y = 0) { {m_X = x, m_Y = y; } }
 virtual ~Coordinate() {}
};

class CPoint : public Coordinate
{
public:
 void ShowPoint() { cout << "Point at (" << m_X << ',' << m_Y << ')' << endl; }
};

int main()
{
 Coordinate *Pt = new Coordinate;
 CPoint *pPoint = static_cast<CPoint *>(Pt); // 這叫變形

 pPoint = new CPoint;
 Pt = pPoint;
 pPoint = static_cast<CPoint *>(Pt); // 這叫轉形

 return 0;
}

Pt 有沒有 CPoint 成份很重要,先在這釐清,避免再各說各話

>另外要談到名詞對錯,把static_cast稱入強制轉型,才真的是錯的。
>C++的強制轉型是reinterpret_cast

我知道,你就先別管這個,越說越沒交集
作者 : sunyear(coco) VC++卓越專家C++頂尖高手貼文超過2000則
[ 貼文 2421 | 人氣 1485 | 評價 6060 | 評價/貼文 2.5 | 送出評價 5 次 ] 
[ 給個讚 ]  [ 給個讚 ]  [ 回應本文 ]  [ 發表新文 ]  [ 回上頁 ] [ 回討論區列表 ] [ 回知識入口 ]
2017/12/9 下午 06:36:46
>Pt 有沒有 CPoint 成份很重要,先在這釐清,避免再各說各話
什麼成份,別人不懂。
若說血緣關係,前面我己經解釋過了。
類別分成兩部份,資料和操作。
所謂物件實體內容,就是指資料而己。因為CPoint類別並沒有自己的資料成員,所以
CPoint物件和Coordinate物件(資料結構)是完全一樣的。
相同的物件(資料)Pt可以在 Coordinate,當然也可以在CPoint操作。
(本來這兩個類別設計都是在操作相同的資料)
要怎麼告訴編譯器,程式員想用CPoint還是Coordinate操作資料?
static_cast貼上標籤就是一個方式。
對不對,驗證程式所有的功能無誤就是對的。
Pt貼上上CPoint標籤後,除了type information裡面的標籤沒變之外,它就是百分之一百的CPoint物件。
type information沒變有沒什麼影響?和沒虛擬函式的類別一樣,就RTTI裡的type_information不可靠而己,
作者 : sunyear(coco) VC++卓越專家C++頂尖高手貼文超過2000則
[ 貼文 2421 | 人氣 1485 | 評價 6060 | 評價/貼文 2.5 | 送出評價 5 次 ] 
[ 給個讚 ]  [ 給個讚 ]  [ 回應本文 ]  [ 發表新文 ]  [ 回上頁 ] [ 回討論區列表 ] [ 回知識入口 ]
2017/12/9 下午 10:11:57
>CPoint *pPoint = static_cast<CPoint *>(Pt); // 這叫變形
>pPoint = static_cast<CPoint *>(Pt); // 這叫轉形
然後呢?我就不介意你要叫它什麼。
對我來說,程式邏輯就是相同的資料結構可以被賦予不同的操作。
事實上也可以運作無誤。至於要叫它什麼,我都沒意見。
作者 : ccl0504(手動程式產生器) 貼文超過200則
[ 貼文 448 | 人氣 211 | 評價 910 | 評價/貼文 2.03 | 送出評價 2 次 ] 
[ 給個讚 ]  [ 給個讚 ]  [ 回應本文 ]  [ 發表新文 ]  [ 回上頁 ] [ 回討論區列表 ] [ 回知識入口 ]
2017/12/9 下午 11:55:56

> clone 就是要全部複製,若不覆寫就只會 clone 上半部,下半部不見了,自然 dynamic_cast<Derived3> 就會得到 NULL
> 我只是用 dynamic_cast 來證明 clone_pBase2 因 Derived2 沒有複寫 Clone() 會造成複製不完全而已,沒有要這樣使

dynamic_cast (type) 與 clone (data) 完整與否應該屬於兩件不同的事情 ?


>在此之前我認為 dynamic_cast 絕對正確,否則 RTTI 的價值將一夕
>之間瓦解,RTTI 本來就是做 class type 的關係判斷用的,必須要絕對可靠

那我惡搞一下 dynamic_cast 好了, 在 Visual Studio 2017 with RTTI enabled, pA2 有被轉型


#include <stdio.h>
#include <iostream>

using namespace std;

class A1
{
public :
int data1;
public:
virtual ~A1() {}
virtual void Print()
{
cout << "A1" << endl;
}
};

class A2
{
public:
int dataC;
int data1;
public:
virtual ~A2() {}
};

class B1 :public A1
{
};

class B2 : public A2
{
};

int main()
{
A1 *pB1 = new B1;
pB1->data1 = 500;

cout << (void *)pB1 << endl;

A2 *pA2 = dynamic_cast<A2*>((B2 *)pB1);
if (pA2)
{
cout << (void *)pA2 << endl;
cout << pA2->dataC << " " << pA2->data1 << endl;
}

cout << "dynamic_cast<A2*>((B2 *)pB1) pA2_2 is B1: " << ((pA2) ? "YES" : "NO") << endl;

return 0;
}




作者 : sunyear(coco) VC++卓越專家C++頂尖高手貼文超過2000則
[ 貼文 2421 | 人氣 1485 | 評價 6060 | 評價/貼文 2.5 | 送出評價 5 次 ] 
[ 給個讚 ]  [ 給個讚 ]  [ 回應本文 ]  [ 發表新文 ]  [ 回上頁 ] [ 回討論區列表 ] [ 回知識入口 ]
2017/12/10 上午 01:23:01
>A2 *pA2 = dynamic_cast<A2*>((B2 *)pB1);
C style cast 相當於 C++的 reinterpret_cast,是一種不顧一切的轉型,轉型後連type information(若有的話)一併更改。
這是真正強姦式轉型,不,言重了....是強制轉型。
static_cast它還是有作安全性檢查的,你這行程式改成static_cast,在編譯階段就會被擋下來:
A2 *pA2 = dynamic_cast<A2*>(static_cast<B2 *>(pB1));
static_cast還是會作基本血緣的檢查,當轉型是從基類轉型衍生類別時,編譯器會過關,否則會擋下來。
這表示,C++在規範時己知有些基類別物件雖不是原生自衍生類別,但會有轉型衍生類別的需要。
(不然就一個dynamic_cast打通關就好了,搞那麼多cast做什麼?)
我那Coordinate並不是惡搞,而是依static_cast精神去使用的,這樣都被C大判死罪了,
你這是犯了強姦罪,不,又說錯了,是強制罪!會被打入十八層地嶽的。哈哈
作者 : ccl0504(手動程式產生器) 貼文超過200則
[ 貼文 448 | 人氣 211 | 評價 910 | 評價/貼文 2.03 | 送出評價 2 次 ] 
[ 給個讚 ]  [ 給個讚 ]  [ 回應本文 ]  [ 發表新文 ]  [ 回上頁 ] [ 回討論區列表 ] [ 回知識入口 ]
2017/12/10 下午 12:08:16


去查了一下 assembly code, 發現 dynamic_cast 是 downcasting 的時候, 才會 call __RTDynamicCast
upcasting 則是與 static_cast 相同, 在 compile 階段就做檢查了

 
#include "stdafx.h"

#include <stdio.h>
#include <iostream>

using namespace std;

class A1
{
public :
int data1;
public:
virtual ~A1() {}
virtual void Print()
{
cout << "A1" << endl;
}
};

class A2
{
public:
int dataC;
int data1;
public:
virtual ~A2() {}
};

class B1 :public A1
{
};

class B2 : public A2
{
};

class C1 : public B1, public B2
{
};

int main()
{
A1 *pB1 = new B1;
pB1->data1 = 500;

cout << (void *)pB1 << endl;

A2 *pA2 = dynamic_cast<A2*>((B2 *)pB1);
if (pA2)
{
cout << (void *)pA2 << endl;
cout << pA2->dataC << " " << pA2->data1 << endl;
}

cout << "dynamic_cast<A2*>((B2 *)pB1) pA2_2 is B1: " << ((pA2) ? "YES" : "NO") << endl;

B1*pA1 = dynamic_cast<B1*>(pB1);

C1 *pC1 = new C1;
B1 *pB1_2 = dynamic_cast<B1*>(pC1);
C1 *pC1_2 = dynamic_cast<C1*>(pB1_2);

B1 *pB1_3 = static_cast<B1*>(pC1);
C1 *pC1_3 = static_cast<C1*>(pB1_2);

return 0;
}



; 63 : B1 *pB1_2 = dynamic_cast<B1*>(pC1);

mov rax, QWORD PTR pC1$[rbp]
mov QWORD PTR pB1_2$[rbp], rax

; 64 : C1 *pC1_2 = dynamic_cast<C1*>(pB1_2);

mov DWORD PTR [rsp+32], 0
lea r9, OFFSET FLAT:??_R0?AVC1@@@8
lea r8, OFFSET FLAT:??_R0?AVB1@@@8
xor edx, edx
mov rcx, QWORD PTR pB1_2$[rbp]
call __RTDynamicCast
mov QWORD PTR pC1_2$[rbp], rax


; 65 :
; 66 : B1 *pB1_3 = static_cast<B1*>(pC1);

mov rax, QWORD PTR pC1$[rbp]
mov QWORD PTR pB1_3$[rbp], rax

; 67 : C1 *pC1_3 = static_cast<C1*>(pB1_2);

mov rax, QWORD PTR pB1_2$[rbp]
mov QWORD PTR pC1_3$[rbp], rax

作者 : ccl0504(手動程式產生器) 貼文超過200則
[ 貼文 448 | 人氣 211 | 評價 910 | 評價/貼文 2.03 | 送出評價 2 次 ] 
[ 給個讚 ]  [ 給個讚 ]  [ 回應本文 ]  [ 發表新文 ]  [ 回上頁 ] [ 回討論區列表 ] [ 回知識入口 ]
2017/12/10 下午 12:30:48

>我那Coordinate並不是惡搞,而是依static_cast精神去使用的,這樣都被C大判死罪了,
>你這是犯了強姦罪,不,又說錯了,是強制罪!會被打入十八層地嶽的。哈哈

別這麼說沒~
實作上有可能把 object 存到 list 上, 讀出的時候型態 cast 錯, 就造成躲過了 complier casting 的檢查
所以除了依賴 compiler 的能力外, programmer 應該也要了解自己在寫什麼吧. ^^

作者 : sunyear(coco) VC++卓越專家C++頂尖高手貼文超過2000則
[ 貼文 2421 | 人氣 1485 | 評價 6060 | 評價/貼文 2.5 | 送出評價 5 次 ] 
[ 給個讚 ]  [ 給個讚 ]  [ 回應本文 ]  [ 發表新文 ]  [ 回上頁 ] [ 回討論區列表 ] [ 回知識入口 ]
2017/12/10 下午 02:36:05
>所以除了依賴 compiler 的能力外, programmer 應該也要了解自己在寫什麼吧. ^^
本來程式對錯就是程式員要負最大的責任,就算編譯得過,所有的動態檢查也沒問題,
還是可以寫一堆不能work的爛程式。
盡信書不如無書;要盡信dynamic_cast不如無dynamic_cast。
dynamic_cast不會過,強制轉型,只要程式員自己知道在做什麼沒什麼不可以。
作者 : sunyear(coco) VC++卓越專家C++頂尖高手貼文超過2000則
[ 貼文 2421 | 人氣 1485 | 評價 6060 | 評價/貼文 2.5 | 送出評價 5 次 ] 
[ 給個讚 ]  [ 給個讚 ]  [ 回應本文 ]  [ 發表新文 ]  [ 回上頁 ] [ 回討論區列表 ] [ 回知識入口 ]
2017/12/10 下午 02:38:02
>dynamic_cast 是 downcasting 的時候, 才會 call __RTDynamicCast
>upcasting 則是與 static_cast 相同, 在 compile 階段就做檢查了
作者 : cxxlman(CxxlMan) C++優秀好手貼文超過1000則
[ 貼文 1003 | 人氣 3227 | 評價 1260 | 評價/貼文 1.26 | 送出評價 27 次 ] 
[ 給個讚 ]  [ 給個讚 ]  [ 回應本文 ]  [ 發表新文 ]  [ 回上頁 ] [ 回討論區列表 ] [ 回知識入口 ]
2017/12/10 下午 06:32:39

>>Pt 有沒有 CPoint 成份很重要,先在這釐清,避免再各說各話
>什麼成份,別人不懂。

用包裝盒做比喻好了,C 比較重視包裝盒的內容物,不在乎包裝盒長怎樣,C++ 則重視在包裝盒,不在乎包的是什麼東西
所以你一直 C 的觀點去看 C++ 永遠也搞不清楚
作者 : cxxlman(CxxlMan) C++優秀好手貼文超過1000則
[ 貼文 1003 | 人氣 3227 | 評價 1260 | 評價/貼文 1.26 | 送出評價 27 次 ] 
[ 給個讚 ]  [ 給個讚 ]  [ 回應本文 ]  [ 發表新文 ]  [ 回上頁 ] [ 回討論區列表 ] [ 回知識入口 ]
2017/12/10 下午 06:39:50

>>CPoint *pPoint = static_cast<CPoint *>(Pt); // 這叫變形
>>pPoint = static_cast<CPoint *>(Pt); // 這叫轉形
>然後呢?我就不介意你要叫它什麼。
>對我來說,程式邏輯就是相同的資料結構可以被賦予不同的操作。
>事實上也可以運作無誤。至於要叫它什麼,我都沒意見。

在 OO 的世界𥚃,要參與哪些活動就要有適當的身份,一個物件能切換哪些身份決定了能參加哪些活動,這樣你能了解變形和轉形意義的不同嗎
作者 : cxxlman(CxxlMan) C++優秀好手貼文超過1000則
[ 貼文 1003 | 人氣 3227 | 評價 1260 | 評價/貼文 1.26 | 送出評價 27 次 ] 
[ 給個讚 ]  [ 給個讚 ]  [ 回應本文 ]  [ 發表新文 ]  [ 回上頁 ] [ 回討論區列表 ] [ 回知識入口 ]
2017/12/10 下午 07:13:55
#include <stdio.h>
#include <iostream>

using namespace std;

class A1
{
public:
 int data1;
public:
 virtual ~A1() {}
 virtual void Print()
 {
  cout << "A1" << endl;
 }
};

class A2
{
public:
 int dataC;
 int data1;
public:
 virtual ~A2() {}
};

class B1 :public A1
{
};

class B2 : public A2
{
};

int main()
{
 A1 *pB1 = new B1;
 pB1->data1 = 500;

 cout << (void *)pB1 << endl;

 A2 *pA2 = dynamic_cast<A2*>((B2 *)pB1);
 if (pA2)
 {
  cout << (void *)pA2 << endl;
  cout << pA2->dataC << " " << pA2->data1 << endl;
 }

 cout << "dynamic_cast<A2*>((B2 *)pB1) pA2_2 is B1: " << ((pA2) ? "YES" : "NO") << endl;

 B2 *pB2 = dynamic_cast<B2*>(pA2);
 cout << "dynamic_cast<B2*>(pA2) pA2 is B1: " << ((pB2) ? "YES" : "NO") << endl; // NO

 return 0;
}
作者 : sunyear(coco) VC++卓越專家C++頂尖高手貼文超過2000則
[ 貼文 2421 | 人氣 1485 | 評價 6060 | 評價/貼文 2.5 | 送出評價 5 次 ] 
[ 給個讚 ]  [ 給個讚 ]  [ 回應本文 ]  [ 發表新文 ]  [ 回上頁 ] [ 回討論區列表 ] [ 回知識入口 ]
2017/12/10 下午 10:15:24
>在 OO 的世界𥚃,要參與哪些活動就要有適當的身份,
>一個物件能切換哪些身份決定了能參加哪些活動,這樣你能了解變形和轉形意義的不同嗎
我們己經兩條沒交集的平行線對話了。
請給你的說法有公信力的文件,我們再來討論原本文件是在說什麼。
另外,不要再嘮叼名詞了,好嗎?我都說了,要叫什麼我沒意見。
我那coordinate的程式,就是一個document有多個不同操作的一種實現。有什麼不合OO?
另一個觀點,就是把資料和方法動態的組合成一個物件。組合的方式也在C++允許的方式,有什麼不合OO?
MFC都可以動態的產生新的類別,難道它更不合OO。OO談的不是怎麼產生類別物件吧。
OO太形而上了,C++只是OO實現的一種程式語言。如果你談的是C++的類別架構,下面的分析看你能不能認同。
一個沒有任何資料只有函式(操作)的類別,可以產生物件嗎?(回頭看我說的,狹義的物件指的是資料不是函式)
C++允許這樣的類別產生物件,但其實這樣的類別己經退化成函式群組的name space了。把函式成員都標上static完全可以當做name space來使用。
(連語法都相同)
即使不標上static,基本上這些函式成員的本質還是static(這是為什麼我說狹義的物件指的是資料不是函式,物件裡面根本沒有函式本體)。
你用只有函式的類別產生一個物件,C++ 為了語言相容性,還是會給你一個物件,但這個物件內容是沒意義的。
只有函式的類別產生一個物件的指標,可隨你指定任何值,甚至是null,它都可以運作無誤。所以你可以把null 轉型成這樣的類別物件去操作。
這說明了只有函式的類別己退化成name space等級的事實。
如果這個沒問題,就可以往下看。
如果有一群函式是用來操作座標(向量)顯示成圖形的。另一群函式是用來操作座標本文說明的,另一群函式是把座標做三角函式操作........
可不可以把這些群組使用name space分門別類標示名稱?這沒問題吧?
好了,進入問題核心。這些座標操作的群組是要操作座標這種形態的資料的,怎麼傳遞資料給函使運算或操作?
這簡單,就把座標資料做成物件以引數的方式傳給函式就可以了。
目的雖達到了,但這不合OO的精神是吧,最主要是函式沒綁定資料,資料的連結還要靠其它的機制去管理。
既然C++類別是由函式去操作資料,那是不是把座標物件和函式群組結合成一個類別,這樣函式群組就有一個綁定的資料可以操作了?
但我又想要"動態"的指定座標要用那一個群組函式,怎麼辦?
一顆鷄蛋隨心著心情好壞,可能要煎要炒或煮(湯)
能不能把煎蛋,炒蛋或蛋花湯,分成材料的蛋,和煎,炒,煮三個操作群組?
這算是不錯的主意,所以把座標資料做成一個資料物件,而操作的函式群組就分類成純操作類別?(要記得只有操作函式的類別,是static 操作性質,物件沒有意義)
然後就可以把座標物件和操作函式,動態結合成所要的類別物件。如座標圖形顯示物件,座標三角函式物件等等。
若以前面蛋的煎,炒,煮的動態結合就是煎蛋,炒蛋和蛋花湯。
好像有點在解說decorator pattern ^^
無論如何,這樣的思維,和OO沒什麼不合之處。只是在C++結合我方式是採用轉形方式實現而己。
作者 : sunyear(coco) VC++卓越專家C++頂尖高手貼文超過2000則
[ 貼文 2421 | 人氣 1485 | 評價 6060 | 評價/貼文 2.5 | 送出評價 5 次 ] 
[ 給個讚 ]  [ 給個讚 ]  [ 回應本文 ]  [ 發表新文 ]  [ 回上頁 ] [ 回討論區列表 ] [ 回知識入口 ]
2017/12/11 上午 12:37:16
>在 OO 的世界𥚃,要參與哪些活動就要有適當的身份,
>一個物件能切換哪些身份決定了能參加哪些活動,這樣你能了解變形和轉形意義的不同嗎
你要叫static_cast是轉型還是變形,魔法還是蛻變,我都沒意見,重覆了多次,還不能了我沒在爭名詞嗎?
不管變形還是蛻變,都是身份己經改變了,它的新身份可以參加那些活動,它就是以適當的身份在參加。
若是不能,就算編譯時沒能擋下,執行也會出錯。
作者 : sunyear(coco) VC++卓越專家C++頂尖高手貼文超過2000則
[ 貼文 2421 | 人氣 1485 | 評價 6060 | 評價/貼文 2.5 | 送出評價 5 次 ] 
[ 給個讚 ]  [ 給個讚 ]  [ 回應本文 ]  [ 發表新文 ]  [ 回上頁 ] [ 回討論區列表 ] [ 回知識入口 ]
2017/12/11 上午 10:41:47
>這算是不錯的主意,所以把座標資料做成一個資料物件,而操作的函式群組就分類成純操作類別?
(要記得只有操作函式的類別,是static 操作性質,物件沒有意義)
>然後就可以把座標物件和操作函式,動態結合成所要的類別物件。如座標圖形顯示物件,座標三角函式物件等等。
>若以前面蛋的煎,炒,煮的動態結合就是煎蛋,炒蛋和蛋花湯。
喔,早上再想了一下,你這麼介意名詞。
我前面講的,操作函式並不會影響物件的論述,若你能同意。
那麼相同的物件,要被不同的純操作類別(只有函式的類別),相當於name space的using。
可惜,C++語言,這種類name space的using的語法並不是using,它還是以cast去實現。
我把前面coordinate加上註解(只有main的程式),這樣你是否能釋懷?
class Coordinate
{
protected:
double m_X;
double m_Y;
public:
Coordinate(double x, double y) { {m_X = x, m_Y = y; } }
virtual ~Coordinate() {}
};

class CPoint : public Coordinate
{
public:
void ShowPoint() { cout << "Point at (" << m_X <<','<< m_Y<<')' << endl; }
};

class CTan : public Coordinate
{
public:
void ShowTangent() {
cout << "Tangent is " << (m_Y / m_X) << endl;}
};

class CDrawPoint : public CPoint
{
public:
void DrawPoint() {
ShowPoint();
//Draw the point...
}
};


int main()
{
Coordinate Pt(12.0, 22.8);
CPoint *pPoint = static_cast<CPoint *>(& Pt);//use CPoint to operate Pt
pPoint->ShowPoint();
CTan *pTangent = static_cast< CTan *>(&Pt);//use CTan to operate Pt
pTangent->ShowTangent();
CDrawPoint *pDrawPoint = static_cast< CDrawPoint *>(&Pt);//use CDrawPointto operate Pt
pDrawPoint->DrawPoint();
return 0;
}
或你還是比較喜歡變形名詞,也把註記改變形,若能令伙滿意,我也沒意見:
nt main()
{
Coordinate Pt(12.0, 22.8);
CPoint *pPoint = static_cast<CPoint *>(& Pt);//變形成CPoint以操作Pt
pPoint->ShowPoint();
CTan *pTangent = static_cast< CTan *>(&Pt);//變形成pTangent以操作Pt
pTangent->ShowTangent();
CDrawPoint *pDrawPoint = static_cast< CDrawPoint *>(&Pt);//變形成CDrawPoint 以操作Pt
pDrawPoint->DrawPoint();
return 0;
}
作者 : cxxlman(CxxlMan) C++優秀好手貼文超過1000則
[ 貼文 1003 | 人氣 3227 | 評價 1260 | 評價/貼文 1.26 | 送出評價 27 次 ] 
[ 給個讚 ]  [ 給個讚 ]  [ 回應本文 ]  [ 發表新文 ]  [ 回上頁 ] [ 回討論區列表 ] [ 回知識入口 ]
2017/12/11 上午 11:48:28
起因是我用 dynamic_cast 來檢驗 Clone() 產生的分身是不是分身,
而你認為即使 dynamic_cast 檢驗不過還是有可能仍為分身,
理由竟然是用 static_cast 仍然可 "轉型" 為分身,
因此引暴對轉形的爭議,所以我不得不用 "變型" 來定義你所謂的轉型
如果在這部份沒辦法取得共識,其他都是各說各話沒有意義
作者 : sunyear(coco) VC++卓越專家C++頂尖高手貼文超過2000則
[ 貼文 2421 | 人氣 1485 | 評價 6060 | 評價/貼文 2.5 | 送出評價 5 次 ] 
[ 給個讚 ]  [ 給個讚 ]  [ 回應本文 ]  [ 發表新文 ]  [ 回上頁 ] [ 回討論區列表 ] [ 回知識入口 ]
2017/12/11 下午 01:22:27
>而你認為即使 dynamic_cast 檢驗不過還是有可能仍為分身,
>理由竟然是用 static_cast 仍然可 "轉型" 為分身,
我從來沒有用分身這字眼,那是你自己對cast或靜態轉型的解讀。
 static_cast在C++中文翻譯就是靜態轉型,不然你要我用什麼名詞,我也不介意。
就算一開始你不知我用什麼名詞,我都聲明那麼多次我不介意用什麼名詞了,還不了解嗎?
重點不是名詞,而是我那static_cast的程式是是可以運作無誤,也有它邏輯含意(不是惡搞),這樣還有什麼問題?
從2017/11/22 上午 01:20:31貼文開始
<coco>
而我前面提出的問題是,若子類別沒增加跟clone 有關的東西,為什麼要被強迫實作?

然後你就開始,什麼只複製一半,複製不完全。
接下來的討論,幾乎我一直再跟你解釋,衍伸類別若沒增加跟物件(資料)有關的東西,產生的物件就是跟基類別物件是一樣的。
這時後的基類物件==衍生物件,複製是完全的,沒什麼只複製一半的問題。
再這種情況,產生的基類別物件(轉型)給衍生類別操作是沒有問題的。
接下來的一整串討論幾乎是,你一直在強調dynamic_cast不過就是錯的。
我一直解釋,dynamic_cast不過,只是type information比對不過,編譯器不保證安全,程式員要自行負責。
你可以再從2017/11/22 上午 01:20:31開始看,是不是都在這點上繞。
作者 : cxxlman(CxxlMan) C++優秀好手貼文超過1000則
[ 貼文 1003 | 人氣 3227 | 評價 1260 | 評價/貼文 1.26 | 送出評價 27 次 ] 
[ 給個讚 ]  [ 給個讚 ]  [ 回應本文 ]  [ 發表新文 ]  [ 回上頁 ] [ 回討論區列表 ] [ 回知識入口 ]
2017/12/11 下午 01:56:47

>>而你認為即使 dynamic_cast 檢驗不過還是有可能仍為分身,
>>理由竟然是用 static_cast 仍然可 '轉型' 為分身,
>我從來沒有用分身這字眼,那是你自己對cast或靜態轉型的解讀。

分身是 Clone() 功能,這部份有問題嗎

> static_cast在C++中文翻譯就是靜態轉型,不然你要我用什麼名詞,我也不介意。
>就算一開始你不知我用什麼名詞,我都聲明那麼多次我不介意用什麼名詞了,還不了解嗎?

我不是在和你爭名詞,只是你和 oo 的轉型概念混在一起了,我只是要把它分割清楚

>重點不是名詞,而是我那static_cast的程式是是可以運作無誤,也有它邏輯含意(不是惡搞),這樣還有什麼問題?

重點就在這了,你真把它混在一起了

>從2017/11/22 上午 01:20:31貼文開始
><coco>
>而我前面提出的問題是,若子類別沒增加跟clone 有關的東西,為什麼要被強迫實作?

C 可以 C++ 就是不行

>然後你就開始,什麼只複製一半,複製不完全。
>接下來的討論,幾乎我一直再跟你解釋,衍伸類別若沒增加跟物件(資料)有關的東西,產生的物件就是跟基類別物件是一樣的。

再明確告訴你,不能這樣搞

>這時後的基類物件==衍生物件,複製是完全的,沒什麼只複製一半的問題。
>再這種情況,產生的基類別物件(轉型)給衍生類別操作是沒有問題的。
>接下來的一整串討論幾乎是,你一直在強調dynamic_cast不過就是錯的。
>我一直解釋,dynamic_cast不過,只是type information比對不過,編譯器不保證安全,程式員要自行負責。
>你可以再從2017/11/22 上午 01:20:31開始看,是不是都在這點上繞。

這很重要所以要說三次,絕對不能這樣搞

oo 講究封裝,對黑箱裡的東西不在乎,也不該去在乎,
所以兩個 class 內容都一模一樣,oo 還是要視為不同,
所以你用 static_cast 的手法,做為 "即使 dynamic_cast 檢驗不過還是有可能仍為分身" 的理由,
oo 是不能接受的

作者 : cxxlman(CxxlMan) C++優秀好手貼文超過1000則
[ 貼文 1003 | 人氣 3227 | 評價 1260 | 評價/貼文 1.26 | 送出評價 27 次 ] 
[ 給個讚 ]  [ 給個讚 ]  [ 回應本文 ]  [ 發表新文 ]  [ 回上頁 ] [ 回討論區列表 ] [ 回知識入口 ]
2017/12/11 下午 02:17:35

>>從2017/11/22 上午 01:20:31貼文開始
>><coco>
>>而我前面提出的問題是,若子類別沒增加跟clone 有關的東西,為什麼要被強迫實作?
>
>C 可以 C++ 就是不行
>

這段我回太快

必須強迫 clone() 實作的原因是因有新延伸的類別,就必須要讓分身也要有相同的類別,
也就是 type 須一致,不是內容要一樣
作者 : cxxlman(CxxlMan) C++優秀好手貼文超過1000則
[ 貼文 1003 | 人氣 3227 | 評價 1260 | 評價/貼文 1.26 | 送出評價 27 次 ] 
[ 給個讚 ]  [ 給個讚 ]  [ 回應本文 ]  [ 發表新文 ]  [ 回上頁 ] [ 回討論區列表 ] [ 回知識入口 ]
2017/12/11 下午 02:22:11

>
>>>從2017/11/22 上午 01:20:31貼文開始
>>><coco>
>>>而我前面提出的問題是,若子類別沒增加跟clone 有關的東西,為什麼要被強迫實作?
>>
>>C 可以 C++ 就是不行
>>
>
>這段我回太快
>
>必須強迫 clone() 實作的原因是因有新延伸的類別,就必須要讓分身也要有相同的類別,
>也就是 type 須一致,不是內容要一樣
>
這樣講也不對,要改一下
內容一樣外,type 也要一致,不然怎麼會叫 clone ^^
作者 : sunyear(coco) VC++卓越專家C++頂尖高手貼文超過2000則
[ 貼文 2421 | 人氣 1485 | 評價 6060 | 評價/貼文 2.5 | 送出評價 5 次 ] 
[ 給個讚 ]  [ 給個讚 ]  [ 回應本文 ]  [ 發表新文 ]  [ 回上頁 ] [ 回討論區列表 ] [ 回知識入口 ]
2017/12/11 下午 03:34:51
>分身是 Clone() 功能,這部份有問題嗎
只要不硬塞"分身"是我說的,就沒問題。
非必要,我不喜歡創造名詞,用己定義明確的,大家慣用都懂的比較好。
但你要用,我不介意,只要"變形"成是我說的就好。
對我來說clone應是複製,複製什麼?當然是複製物件實體,對吧?
在我2017/11/22 上午 01:20:31貼的
Derived3 的物件實體和 Base是一模一樣的,所以複製一個Base實體給Derived3,有什麼問題嗎?
至於Base實體要如何轉成給Derived3操作,那是語法的問題,無關OO。

>我不是在和你爭名詞,只是你和 oo 的轉型概念混在一起了,我只是要把它分割清楚
OO轉型規則就是A實體的集合包含或等於B實體集合,就可以轉。
Derived3 的實體的集合和 Base體集合是一樣的,為什麼不能轉?
是你一直沒能把物件實體(資料)和函式(操作)成員分割清楚吧。

>>重點不是名詞,而是我那static_cast的程式是是可以運作無誤,也有它邏輯含意(不是惡搞),這樣還有什麼問題?
>重點就在這了,你真把它混在一起了
跟誰混在一起了?程式結構和邏輯都分析給你了,有沒有違背OO是看程式結構和邏輯,不是看有沒有使用static_cast。
static_cast只是手段,也可以運作。就算跟什麼混在一起也由得你說(搞不清跟什麼混在一起),只要不說它是錯的不能使用就好。

>再明確告訴你,不能這樣搞
不好意思,若不能這樣搞,以前寫的通訊程式,大概都難產了!
一個收取經通訊傳來的命令(或信息),底層只管封包收取,不管內容是什麼命令(或信息)。
這樣的封包基本上就是UINT_8陣列的指標。傳到上層,會先把它強制轉型成raw data type例如:
struct RawCmdData
{
UINT_8 m_Cmd://command identify
UINT_8 m_Para[MAX_CMDLEN];
};
傳到最上層命令令處理程序時要依m_u8Cmd是那一種命令,再強制轉型成相對應的命令列別,例如:
struct CMD_Copy
{
UINT_8 m_Cmd://copy command id
UINT_32 m_SrcAddr;
UINT_32 m_DstAddr;
UINT_32 m_DataLength;
}

struct CMD_Write
{
UINT_8 m_Cmd://write command id
UINT_32 m_DstAddr;
UINT_32 m_DataLength;
UINT_8 m_Data[MAX_CMDLEN];
}

:::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::
請問,如果不強制轉型,我怎麼寫類似這樣的程式?
通訊傳過來的資料都不是原生自RawCmdData, CMD_Copy或CMD_Write。
dynamic_cast通不過(連編譯都過不了),不強制轉型,用你的OOP觀念,是要怎麼寫?
這種狀況都是經由通訊協議,程式判斷傳過來的封包資料是屬於那一種資料結構,就強制轉型。
何況我前面的例子,都是寫程式時就知 Derived3和Base是同一個資料結構,怎會不能轉?

>這很重要所以要說三次,絕對不能這樣搞
好吧,我同意還沒長牙齒,就不要啃甘蔗。我只能同意到這裡了。
但很抱歉,我寫的程式有一半以上和通訊有關,不這樣樣搞,應該早沒頭路了 ^^

>所以兩個 class 內容都一模一樣,oo 還是要視為不同,
不是兩個class內容都一模一樣,而是兩個class的實體是同一個類別物件。
再看一次我的例子!

>所以你用 static_cast 的手法,做為 "即使 dynamic_cast 檢驗不過還是有可能仍為分身" 的理由,
>oo 是不能接受的
我不是這樣說,不要又扭曲別人的意思。
我說的是程式員確認兩個類別的物件(資料實體)是一樣的,就可以互轉。
OOP正確的說法應是:A實體的集合包含或等於B實體集合,就可以A轉形成B來使用。
基類和衍生類物件的轉型規則,是符合上一句規則且是編譯器可以檢查的例子。
超出編譯器可以檢查的範圍,但仍符合可以轉型的case,就是程式員自行負責。
不然有的程式真的寫不出來。
作者 : sunyear(coco) VC++卓越專家C++頂尖高手貼文超過2000則
[ 貼文 2421 | 人氣 1485 | 評價 6060 | 評價/貼文 2.5 | 送出評價 5 次 ] 
[ 給個讚 ]  [ 給個讚 ]  [ 回應本文 ]  [ 發表新文 ]  [ 回上頁 ] [ 回討論區列表 ] [ 回知識入口 ]
2017/12/11 下午 03:50:22
>必須強迫 clone() 實作的原因是因有新延伸的類別,就必須要讓分身也要有相同的類別,
如果延伷類別實體和基類一樣呢,可不可讓基類clone就好?
clone應該是複製資料的值不是建立物件,你那個clone在現實中,應該是要外部建立物件,再丟進clone去複製資料。
想想看,實際應用,衍生類clone不能呼叫基類的clone去完成資料複製,那是不是要由衍生類clone幫基類clone做複製的工作?
這合理嗎?若基類有資料是private的,那衍生類怎麼做基類該做的工作。
這本來就不是合於OOP精神的東西。只是硬弄一個好像所有的衍生類別都要自行撰寫clone的例子,但實際不太可行,也不應該這樣寫。






作者 : cxxlman(CxxlMan) C++優秀好手貼文超過1000則
[ 貼文 1003 | 人氣 3227 | 評價 1260 | 評價/貼文 1.26 | 送出評價 27 次 ] 
[ 給個讚 ]  [ 給個讚 ]  [ 回應本文 ]  [ 發表新文 ]  [ 回上頁 ] [ 回討論區列表 ] [ 回知識入口 ]
2017/12/11 下午 03:51:51
不談太多,就重點討論

>>我不是在和你爭名詞,只是你和 oo 的轉型概念混在一起了,我只是要把它分割清楚
>OO轉型規則就是A實體的集合包含或等於B實體集合,就可以轉。

oo 的轉型的意思是,A 所代表的物件包含有 B 這個 type 就可以"切換"過去,我不是要創造名詞,只是要表達清除

>Derived3 的實體的集合和 Base體集合是一樣的,為什麼不能轉?
>是你一直沒能把物件實體(資料)和函式(操作)成員分割清楚吧。

就轉型而言,oo 不會去管實體(資料)和函式(操作),只要物件含有想要 type 就可以"切換"過去
作者 : cxxlman(CxxlMan) C++優秀好手貼文超過1000則
[ 貼文 1003 | 人氣 3227 | 評價 1260 | 評價/貼文 1.26 | 送出評價 27 次 ] 
[ 給個讚 ]  [ 給個讚 ]  [ 回應本文 ]  [ 發表新文 ]  [ 回上頁 ] [ 回討論區列表 ] [ 回知識入口 ]
2017/12/11 下午 03:55:29

>>必須強迫 clone() 實作的原因是因有新延伸的類別,就必須要讓分身也要有相同的類別,
>如果延伷類別實體和基類一樣呢,可不可讓基類clone就好?

不行,type 也要一致
作者 : sunyear(coco) VC++卓越專家C++頂尖高手貼文超過2000則
[ 貼文 2421 | 人氣 1485 | 評價 6060 | 評價/貼文 2.5 | 送出評價 5 次 ] 
[ 給個讚 ]  [ 給個讚 ]  [ 回應本文 ]  [ 發表新文 ]  [ 回上頁 ] [ 回討論區列表 ] [ 回知識入口 ]
2017/12/11 下午 04:19:16
>Derived3 的物件實體和 Base是一模一樣的,所以複製一個Base實體給Derived3,有什麼問題嗎?
這句話我修正為:
Derived3 的物件實體就是 Base,所以複製一個Base實體給Derived3,有什麼問題嗎?
免得你又誤解成兩個不同類類別,只是內容一樣。
其實兩個類別,實體內容一樣,互轉也是可以運作。只是程式這樣寫沒有意義,也是極度不好,static_cast也不會過。
真有想不到的合理應用,那得用reinterpret_cast才能轉得了。
作者 : sunyear(coco) VC++卓越專家C++頂尖高手貼文超過2000則
[ 貼文 2421 | 人氣 1485 | 評價 6060 | 評價/貼文 2.5 | 送出評價 5 次 ] 
[ 給個讚 ]  [ 給個讚 ]  [ 回應本文 ]  [ 發表新文 ]  [ 回上頁 ] [ 回討論區列表 ] [ 回知識入口 ]
2017/12/11 下午 04:23:49
>不行,type 也要一致
type 一致還需轉型嗎?
你是說,type information要一致吧?
你要真這麼堅持,使用reinterpret_cast可以達到你的要求。
徹頭徹尾的改,連type information都會一致。
作者 : sunyear(coco) VC++卓越專家C++頂尖高手貼文超過2000則
[ 貼文 2421 | 人氣 1485 | 評價 6060 | 評價/貼文 2.5 | 送出評價 5 次 ] 
[ 給個讚 ]  [ 給個讚 ]  [ 回應本文 ]  [ 發表新文 ]  [ 回上頁 ] [ 回討論區列表 ] [ 回知識入口 ]
2017/12/11 下午 04:33:18
>oo 的轉型的意思是,A 所代表的物件包含有 B 這個 type 就可以"切換"過去,我不是要創造名詞,只是要表達清除
我的例子Base 物件就是包含(等於)Derived 3,那有什麼問題?
我一直解釋物件是指資料實體,函式是經由類別辨識去呼叫,和物件無關。你一直不回應,但覆文好像一直忽略這件事。
Derived 3的物件實體就是Base,互轉有什麼問題?
把Base cast成Derived3的效應只是可以使用Derived3的函式去操作Base物件罷。
作者 : sunyear(coco) VC++卓越專家C++頂尖高手貼文超過2000則
[ 貼文 2421 | 人氣 1485 | 評價 6060 | 評價/貼文 2.5 | 送出評價 5 次 ] 
[ 給個讚 ]  [ 給個讚 ]  [ 回應本文 ]  [ 發表新文 ]  [ 回上頁 ] [ 回討論區列表 ] [ 回知識入口 ]
2017/12/11 下午 04:47:54
>就轉型而言,oo 不會去管實體(資料)和函式(操作),只要物件含有想要 type 就可以"切換"過去
那不是oo啦!
那只是C++的安全檢查,因為只用type information並不包括所有可以轉型的狀況,所以才有分級轉型。
可以轉型的狀況,但dynamic_cast轉不過去,就使用static_cast轉;
可以轉型的狀況,但static_cast轉不過去,就使用reinterpret_cast轉。
C++轉型規則就是這樣,你硬要說只能dynamic_cast去轉,轉不過去就是不能轉。
那C++ 為何還要設計static_cast和reinterpret_cast?
這個我也問了多次,你從不回答。
作者 : cxxlman(CxxlMan) C++優秀好手貼文超過1000則
[ 貼文 1003 | 人氣 3227 | 評價 1260 | 評價/貼文 1.26 | 送出評價 27 次 ] 
[ 給個讚 ]  [ 給個讚 ]  [ 回應本文 ]  [ 發表新文 ]  [ 回上頁 ] [ 回討論區列表 ] [ 回知識入口 ]
2017/12/12 上午 10:53:40

>>不行,type 也要一致
>type 一致還需轉型嗎?
>你是說,type information要一致吧?
>你要真這麼堅持,使用reinterpret_cast可以達到你的要求。
>徹頭徹尾的改,連type information都會一致。

我回答的是 clone() 的分身和本尊要一致
作者 : cxxlman(CxxlMan) C++優秀好手貼文超過1000則
[ 貼文 1003 | 人氣 3227 | 評價 1260 | 評價/貼文 1.26 | 送出評價 27 次 ] 
[ 給個讚 ]  [ 給個讚 ]  [ 回應本文 ]  [ 發表新文 ]  [ 回上頁 ] [ 回討論區列表 ] [ 回知識入口 ]
2017/12/12 上午 10:59:16

>>oo 的轉型的意思是,A 所代表的物件包含有 B 這個 type 就可以'切換'過去,我不是要創造名詞,只是要表達清除
>我的例子Base 物件就是包含(等於)Derived 3,那有什麼問題?

你忽略了 oo 最講究的 type,不是只看包裹的內容

>我一直解釋物件是指資料實體,函式是經由類別辨識去呼叫,和物件無關。你一直不回應,但覆文好像一直忽略這件事。
>Derived 3的物件實體就是Base,互轉有什麼問題?
>把Base cast成Derived3的效應只是可以使用Derived3的函式去操作Base物件罷。
作者 : cxxlman(CxxlMan) C++優秀好手貼文超過1000則
[ 貼文 1003 | 人氣 3227 | 評價 1260 | 評價/貼文 1.26 | 送出評價 27 次 ] 
[ 給個讚 ]  [ 給個讚 ]  [ 回應本文 ]  [ 發表新文 ]  [ 回上頁 ] [ 回討論區列表 ] [ 回知識入口 ]
2017/12/12 上午 11:08:09

>>就轉型而言,oo 不會去管實體(資料)和函式(操作),只要物件含有想要 type 就可以'切換'過去
>那不是oo啦!
>那只是C++的安全檢查,因為只用type information並不包括所有可以轉型的狀況,所以才有分級轉型。
>可以轉型的狀況,但dynamic_cast轉不過去,就使用static_cast轉;
>可以轉型的狀況,但static_cast轉不過去,就使用reinterpret_cast轉。
>C++轉型規則就是這樣,你硬要說只能dynamic_cast去轉,轉不過去就是不能轉。
>那C++ 為何還要設計static_cast和reinterpret_cast?
>這個我也問了多次,你從不回答。

所以我才用變型和轉型來做分割,那是不同領域,不同概念下提供不同的功能給你做選擇,不能互相取待,別搞混了

想想 oo 的最根本精神是什麼,想想高階語言的訴求是什麼,再和你採用的技術做對照


作者 : sunyear(coco) VC++卓越專家C++頂尖高手貼文超過2000則
[ 貼文 2421 | 人氣 1485 | 評價 6060 | 評價/貼文 2.5 | 送出評價 5 次 ] 
[ 給個讚 ]  [ 給個讚 ]  [ 回應本文 ]  [ 發表新文 ]  [ 回上頁 ] [ 回討論區列表 ] [ 回知識入口 ]
2017/12/12 下午 05:23:19
>所以我才用變型和轉型來做分割,那是不同領域,不同概念下提供不同的功能給你做選擇,不能互相取待,別搞混了
一下魚目混珠,一下不能互相取代。我那裡魚目混珠?
不過就Coordinate那個程式,我認為可以用static_cast達成目的,你認為不能這樣搞,不是嗎?
至於互相取代,不知何所指?
如果一個upcast,程式員認為用dynamic_cast比static_cast代的安全性好,為何不能取代?
如果因為效能考量,程式員認為他可以為轉形負責,static_cast取代dynamic_cast,又有何不可?
如果在dynamic_cast通不過,但程式員認為轉換沒有問題,所以使用static_cast或reinterpret_cast,為什麼不行?(這種轉換,你要叫什麼我沒意見)

>想想 oo 的最根本精神是什麼,想想高階語言的訴求是什麼,再和你採用的技術做對照
我們前面談的是C++,不是OO(己經提示過很多次),C++是依OOP規則而來,OOP的基楚當然是OO。
從繼承做比喻,OOP繼承自OO,C++繼承自OOP(不曉得這會不會不倫不類,但應知所指)
要從OO談起也可以,這個討論串大概可以表達我對OO的覯點:
http://www.programmer-club.com.tw/showSameTitleN/c/42739.html

從OO 的觀點,顏色可以是物件,汽車也是物件,其子集也可以物件,並結合成另類物件。
所以紅色(繼承自顏色)的汽車(繼承自汽車)是物件,藍色的汽車(繼承自汽車)也是物件。
轉換1.所以紅色的汽車A,說它是汽車A(upcast),當然可以。
轉換2.那汽車A,說它是紅色的汽車A(Downcast),可不可以?可以,因為汽車A原本(原生)就是紅色的車子。
轉換3.一輀汽車B(沒上顏色),可不可以說它是紅色的汽車A(Downcast)?基本上不可以。但若經上紅漆後,就可以。
轉換4.那紅色的汽車說它是藍色汽車,可不可以?好像不可以吧!但經重新烤上紅漆後,可不可以?
轉換5.那紅色的汽車說它是藍色的摩托車可不可以?太扯了,是不是?但如果紅色的汽車經重新改造成兩輪機動車和上紅漆呢?
風險越往下是越高,OO沒有說那個層級的轉換不可以。OO講的是結構,原理,不是談風險。

downcast來看C++ 吧!
如果依你的物件形別轉換觀點,只有轉換1.和轉換2.才是合法可行的,其它都是惡搞吧(如果我沒誤會你的意思)。
但C++顯然不是依C大的觀點訂定的。C casting在C++分成四種casting。
casting的翻譯,應鑄形或塑形,好像叫塑形比較好(我覺得啦,但用什麼我不介意,以下暫用塑形)。
在C++規範,物件形別的轉換稱之為type conversion(見Standard for Programming Language C++)不是只有塑形而己,
其基本精神,物件形別可不可以轉換,程式員有主控權,並不受限轉換1.和轉換2.。
事實上從轉換1.到轉換5.都是C++允許的範圍。越往下的轉換,風險越高,轉換的風險就是程式員自負。

轉換1.
幾乎確定沒什麼問題,所以可以隱式轉換( 程式員不用聲明,自動轉換),例如:
class B{};
class D:public B{};
B *pB= new D;//不用聲明,就可以轉換

轉換2.
稍具風險,所以需要聲明轉換(讓編譯器確定這是程式員的本意),聲明的方法就是static_cast
class B{};
class D:public B{};
B *pB= new D;//不用聲明,就可以轉換
D* pD= static_cast<D *>pB;//因為是downcast,稍具風險,所以要static_cast聲明
在C++裡,物件裡若有type information,物件形別也可以動態檢查是否屬於轉換1.轉換2.
class B{};
class D:public B{};
B *pB= new D;//不用聲明,就可以轉換
D* pD= dynamic_cast<D *>pB;// dynamic_cast的成功,(相對來說)保證了轉換是在轉換1.轉換2.的相對安全範圍內。

轉換3.
就是本討論串和C大爭議最久的例子,其實它比轉換4.和轉換5.相對安全很多,真的不知道為什麼可以爭論那麼多篇幅。
class B{};
class D:public B{};//D保證不增加資料成員
B *pB= new B;
D* pD= static_cast<D *>pB;//因為D保證不增加資料成員,這個static_cast只是對汽車pB上成D顏色,變成不折不扣,D色的汽車而己
如果這樣寫不可以,那請教C大,改成下面的寫法可不可以?
class B{B(B *pb){}};
class D:public B //D保證不增加資料成員
{
D(pb *):B(pb){};
};
B *pB= new B;
D* pD= new D(pB);//轉換建構子
沒錯,B::B(B *pb){}就叫轉換建構子,在C++標準裡是放在Conversion的章節裡討論。
不知C大對這轉換建構子是否持一樣看法:不可以,不合OOP的精神?
如果轉換建構子可以,這兩種都是C++常用的轉換方式,模構一樣,都是轉換3.的功能。
可以轉換建構子可以,static_cast怎會不可以?因為static_cast看起起來比較冷,所以感冒? ^^
那我把前面的Coordinate改成轉換建構子,是否你就沒有意見了?
就我來說,邏輯,功能完全一樣,只是朝三暮四,一樣的意思。

轉換4.
前面提到的轉換建構子,拿來做轉換3.有點牛刀殺鷄,大材小用。
用它做轉換,轉換3.的衍生類別己經不受"保證不增加資料成員"限制了。
因為轉換物件是原生自衍生類別。至於轉換時,增加的資料成員可以在建構子裡處理。
風險當然比轉換3.高,但就是那一句話,程式員自負風險。C大如果看到這裡要跳腳。請稍安勿噪...
下面的型別轉換,可能才是爆血管級的。:D

轉換5.
先看一下這個例子:
char *str="I am CxxlMan"
string ps= new string(str);
這是C++標準程式館的轉換建構子例子。
是從char *型別轉換成 class string,完全沒血緣關係的轉換。
如果說,那是因為都是字串容器的關係,所以才可轉換。
好像對,其實不對。對編譯器來說,這是完全不同的兩種形態。
是不是字串容器,只有程式員知道,string轉成char *,可不可以轉,轉換有沒有意義,只有程式員知道。
所以...後果自負。
如果有人反駁,那只是個帶參數的建構子,不能算是形別轉換。
在C++標準文件裡,這種建構子是放在type conversion的章節裡介紹的。就像複製建構子,也是帶參數的建構子。
它們在C++稱之為複製建構子,和轉換建構子一樣都是特定的建構子。
但,好吧... 我不爭論。看下面的使用者定義轉換。
C++對塑形是可以重載的,也就是說,鑄形操作要怎麼做,使用者可以自訂義(user define conversion)。
這己經超出塑形的範圍,使用者可以使用塑形的operator,天馬行空的轉換形別物件。
例如
MFC 的CString(類似string)
CString s;
char *= (char *)s;//用casting operator轉換完全沒血緣關係的物件
如何重載casting operator請自行參閱C++語言。
使用者自定義塑形,幾乎可以天馬行空,愛怎麼轉就怎麼轉,完全自由的。
當然,還是後果自負。

看到這裡,應該可以確定C++對轉換是很開放的了吧(或許別的oop沒這麼free)。
如果,有人要因此批評,C++很不OO,或MFC很不OO。
這是另外的議題,但就本樓議題,就談C++。(一談OO就會抽象的東西一大堆跑出來,寫到會抽筋,煩...)
作者 : cxxlman(CxxlMan) C++優秀好手貼文超過1000則
[ 貼文 1003 | 人氣 3227 | 評價 1260 | 評價/貼文 1.26 | 送出評價 27 次 ] 
[ 給個讚 ]  [ 給個讚 ]  [ 回應本文 ]  [ 發表新文 ]  [ 回上頁 ] [ 回討論區列表 ] [ 回知識入口 ]
2017/12/12 下午 07:29:04

>>所以我才用變型和轉型來做分割,那是不同領域,不同概念下提供不同的功能給你做選擇,不能互相取待,別搞混了
>一下魚目混珠,一下不能互相取代。我那裡魚目混珠?
>不過就Coordinate那個程式,我認為可以用static_cast達成目的,你認為不能這樣搞,不是嗎?
>至於互相取代,不知何所指?

請先回頭看,是你要用來證明 dynamuc_cast 不安全,
我只是認為這證明無效

>如果一個upcast,程式員認為用dynamic_cast比static_cast代的安全性好,為何不能取代?
>如果因為效能考量,程式員認為他可以為轉形負責,static_cast取代dynamic_cast,又有何不可?
>如果在dynamic_cast通不過,但程式員認為轉換沒有問題,所以使用static_cast或reinterpret_cast,為什麼不行?(這種轉換,你要叫什麼我沒意見)
>
>>想想 oo 的最根本精神是什麼,想想高階語言的訴求是什麼,再和你採用的技術做對照
>我們前面談的是C++,不是OO(己經提示過很多次),C++是依OOP規則而來,OOP的基楚當然是OO。
>從繼承做比喻,OOP繼承自OO,C++繼承自OOP(不曉得這會不會不倫不類,但應知所指)
>要從OO談起也可以,這個討論串大概可以表達我對OO的覯點:
>http://www.programmer-club.com.tw/showSameTitleN/c/42739.html
>
>從OO 的觀點,顏色可以是物件,汽車也是物件,其子集也可以物件,並結合成另類物件。
>所以紅色(繼承自顏色)的汽車(繼承自汽車)是物件,藍色的汽車(繼承自汽車)也是物件。
>轉換1.所以紅色的汽車A,說它是汽車A(upcast),當然可以。
>轉換2.那汽車A,說它是紅色的汽車A(Downcast),可不可以?可以,因為汽車A原本(原生)就是紅色的車子。
>轉換3.一輀汽車B(沒上顏色),可不可以說它是紅色的汽車A(Downcast)?基本上不可以。但若經上紅漆後,就可以。
>轉換4.那紅色的汽車說它是藍色汽車,可不可以?好像不可以吧!但經重新烤上紅漆後,可不可以?
>轉換5.那紅色的汽車說它是藍色的摩托車可不可以?太扯了,是不是?但如果紅色的汽車經重新改造成兩輪機動車和上紅漆呢?
>風險越往下是越高,OO沒有說那個層級的轉換不可以。OO講的是結構,原理,不是談風險。
>
>downcast來看C++ 吧!
>如果依你的物件形別轉換觀點,只有轉換1.和轉換2.才是合法可行的,其它都是惡搞吧(如果我沒誤會你的意思)。
>但C++顯然不是依C大的觀點訂定的。C casting在C++分成四種casting。
>casting的翻譯,應鑄形或塑形,好像叫塑形比較好(我覺得啦,但用什麼我不介意,以下暫用塑形)。
>在C++規範,物件形別的轉換稱之為type conversion(見Standard for Programming Language C++)不是只有塑形而己,
>其基本精神,物件形別可不可以轉換,程式員有主控權,並不受限轉換1.和轉換2.。
>事實上從轉換1.到轉換5.都是C++允許的範圍。越往下的轉換,風險越高,轉換的風險就是程式員自負。
>
>轉換1.
>幾乎確定沒什麼問題,所以可以隱式轉換( 程式員不用聲明,自動轉換),例如:
>class B{};
>class D:public B{};
>B *pB= new D;//不用聲明,就可以轉換
>
>轉換2.
>稍具風險,所以需要聲明轉換(讓編譯器確定這是程式員的本意),聲明的方法就是static_cast
>class B{};
>class D:public B{};
>B *pB= new D;//不用聲明,就可以轉換
>D* pD= static_cast<D *>pB;//因為是downcast,稍具風險,所以要static_cast聲明
>在C++裡,物件裡若有type information,物件形別也可以動態檢查是否屬於轉換1.轉換2.
>class B{};
>class D:public B{};
>B *pB= new D;//不用聲明,就可以轉換
>D* pD= dynamic_cast<D *>pB;// dynamic_cast的成功,(相對來說)保證了轉換是在轉換1.轉換2.的相對安全範圍內。
>
>轉換3.
>就是本討論串和C大爭議最久的例子,其實它比轉換4.和轉換5.相對安全很多,真的不知道為什麼可以爭論那麼多篇幅。
>class B{};
>class D:public B{};//D保證不增加資料成員
>B *pB= new B;
>D* pD= static_cast<D *>pB;//因為D保證不增加資料成員,這個static_cast只是對汽車pB上成D顏色,變成不折不扣,D色的汽車而己
>如果這樣寫不可以,那請教C大,改成下面的寫法可不可以?
>class B{B(B *pb){}};
>class D:public B //D保證不增加資料成員
>{
>D(pb *):B(pb){};
>};
>B *pB= new B;
>D* pD= new D(pB);//轉換建構子
>沒錯,B::B(B *pb){}就叫轉換建構子,在C++標準裡是放在Conversion的章節裡討論。
>不知C大對這轉換建構子是否持一樣看法:不可以,不合OOP的精神?
>如果轉換建構子可以,這兩種都是C++常用的轉換方式,模構一樣,都是轉換3.的功能。
>可以轉換建構子可以,static_cast怎會不可以?因為static_cast看起起來比較冷,所以感冒? ^^
>那我把前面的Coordinate改成轉換建構子,是否你就沒有意見了?
>就我來說,邏輯,功能完全一樣,只是朝三暮四,一樣的意思。
>
>轉換4.
>前面提到的轉換建構子,拿來做轉換3.有點牛刀殺鷄,大材小用。
>用它做轉換,轉換3.的衍生類別己經不受'保證不增加資料成員'限制了。
>因為轉換物件是原生自衍生類別。至於轉換時,增加的資料成員可以在建構子裡處理。
>風險當然比轉換3.高,但就是那一句話,程式員自負風險。C大如果看到這裡要跳腳。請稍安勿噪...
>下面的型別轉換,可能才是爆血管級的。:D
>
>轉換5.
>先看一下這個例子:
>char *str='I am CxxlMan'
>string ps= new string(str);
>這是C++標準程式館的轉換建構子例子。
>是從char *型別轉換成 class string,完全沒血緣關係的轉換。
>如果說,那是因為都是字串容器的關係,所以才可轉換。
>好像對,其實不對。對編譯器來說,這是完全不同的兩種形態。
>是不是字串容器,只有程式員知道,string轉成char *,可不可以轉,轉換有沒有意義,只有程式員知道。
>所以...後果自負。
>如果有人反駁,那只是個帶參數的建構子,不能算是形別轉換。
>在C++標準文件裡,這種建構子是放在type conversion的章節裡介紹的。就像複製建構子,也是帶參數的建構子。
>它們在C++稱之為複製建構子,和轉換建構子一樣都是特定的建構子。
>但,好吧... 我不爭論。看下面的使用者定義轉換。
>C++對塑形是可以重載的,也就是說,鑄形操作要怎麼做,使用者可以自訂義(user define conversion)。
>這己經超出塑形的範圍,使用者可以使用塑形的operator,天馬行空的轉換形別物件。
>例如
>MFC 的CString(類似string)
>CString s;
>char *= (char *)s;//用casting operator轉換完全沒血緣關係的物件
>如何重載casting operator請自行參閱C++語言。
>使用者自定義塑形,幾乎可以天馬行空,愛怎麼轉就怎麼轉,完全自由的。
>當然,還是後果自負。
>
>看到這裡,應該可以確定C++對轉換是很開放的了吧(或許別的oop沒這麼free)。
>如果,有人要因此批評,C++很不OO,或MFC很不OO。
>這是另外的議題,但就本樓議題,就談C++。(一談OO就會抽象的東西一大堆跑出來,寫到會抽筋,煩...)
>

好長一段,用手機看有點痛苦,所以沒看完,以你的水平應該不會有錯,只要不用錯地方
作者 : sunyear(coco) VC++卓越專家C++頂尖高手貼文超過2000則
[ 貼文 2421 | 人氣 1485 | 評價 6060 | 評價/貼文 2.5 | 送出評價 5 次 ] 
[ 給個讚 ]  [ 給個讚 ]  [ 回應本文 ]  [ 發表新文 ]  [ 回上頁 ] [ 回討論區列表 ] [ 回知識入口 ]
2017/12/12 下午 09:45:37
>請先回頭看,是你要用來證明 dynamuc_cast 不安全,
>我只是認為這證明無效
蛤!?那裡?
你不是一直堅持,dynamic_cast通不過就不能用static_cast取代,用static_cast就是亂搞嗎?
什麼時候變成我說dynamic_cast不安全?我說的是:dynamic_cast 可以通過表示( 相對來說)是安全的,但不表示不通過就是有錯。

>好長一段,用手機看有點痛苦,所以沒看完,以你的水平應該不會有錯,只要不用錯地方
STD lib和MFC的例子,如果有錯,不要找我:D
作者 : cxxlman(CxxlMan) C++優秀好手貼文超過1000則
[ 貼文 1003 | 人氣 3227 | 評價 1260 | 評價/貼文 1.26 | 送出評價 27 次 ] 
[ 給個讚 ]  [ 給個讚 ]  [ 回應本文 ]  [ 發表新文 ]  [ 回上頁 ] [ 回討論區列表 ] [ 回知識入口 ]
2017/12/13 下午 12:23:44

>>請先回頭看,是你要用來證明 dynamuc_cast 不安全,
>>我只是認為這證明無效
>蛤!?那裡?
>你不是一直堅持,dynamic_cast通不過就不能用static_cast取代,用static_cast就是亂搞嗎?

正解

>什麼時候變成我說dynamic_cast不安全?
>

你解釋以下吧

>2017/11/27 下午 10:27:01
>好吧,我改寫一下程式,或許較能澄清
>if (dynamic_cast<Derived4*>(clone_pBase4))
> cout << '安全轉型' << endl;
>else
> cout << '不安全轉型' << endl;
>
>注意: '(不)安全' 不是 '(不)完全'
>所謂'不安全'只是沒辦法保證安全,並不是說轉型一定是錯的。
>如果程式員確定轉型是對的,可以用static_cast來強制轉型。

>我說的是:dynamic_cast 可以通過表示( 相對來說)是安全的,但不表示不通過就是有錯。

RTTI 概念很𥶆單也很確實,有就是有,沒有就是沒有,對就是對,錯就是錯
不通過就是沒有,不通過就是錯,不能用 static_cast 來證明 "不表示不通過就是有錯"

為什麼你我認知會有差異,因𠉎沒理解 oo 重視的是 type,不是內容,
而你一直把內容一樣的 class 視為可互轉的
作者 : sunyear(coco) VC++卓越專家C++頂尖高手貼文超過2000則
[ 貼文 2421 | 人氣 1485 | 評價 6060 | 評價/貼文 2.5 | 送出評價 5 次 ] 
[ 給個讚 ]  [ 給個讚 ]  [ 回應本文 ]  [ 發表新文 ]  [ 回上頁 ] [ 回討論區列表 ] [ 回知識入口 ]
2017/12/13 下午 01:43:13
C>你解釋以下吧
S>>2017/11/27 下午 10:27:01
S>>好吧,我改寫一下程式,或許較能澄清
S>>if (dynamic_cast<Derived4*>(clone_pBase4))
S>> cout << '安全轉型' << endl;
S>>else
S>> cout << '不安全轉型' << endl;
S>>
S>>注意: '(不)安全' 不是 '(不)完全'
S>>所謂'不安全'只是沒辦法保證安全,並不是說轉型一定是錯的。
S>>如果程式員確定轉型是對的,可以用static_cast來強制轉型。
要解釋什麼?這段話如何能理解成我用來"證明 dynamuc_cast 不安全"?
只是說明不安全不表示錯的,只是rtti的機制無法確認安全。
這和我這句話有什麼不同?
S>>我說的是:dynamic_cast 可以通過表示( 相對來說)是安全的,但不表示不通過就是有錯。
程式員要堅持轉型,就是靠自己判斷。C++程式安不安全遇主要還是要靠自己,不要全賴給編譯器。這才是C++精神。


>RTTI 概念很𥶆單也很確實,有就是有,沒有就是沒有,對就是對,錯就是錯
文件講的是"不安全"還是"錯"?兩個的差異,你了解嗎?
如果照你這麼解釋,2017/12/12 下午 05:23:19我貼的轉換3.~轉換5.都不可能在 dynamuc_cast 通過。
std lib和MFC class都有很多例子,你認為它們都是亂搞?

>不通過就是沒有,不通過就是錯,不能用 static_cast 來證明 "不表示不通過就是有錯"
還是胡亂解釋我的話了!我說的是使用static_cast轉型,對錯由程式員自己負責,沒有要證明dynamuc_cast什麼。
至於dynamuc_cast不通過就表示編譯無法確認轉型是安全而己,不表示不通過就是有錯。這是對安不安全的解釋,干static_cast何事?

>為什麼你我認知會有差異,因𠉎沒理解 oo 重視的是 type,不是內容,
type是OOP的東西,不是OO(提示很多次)。OO只談物件(就算提到type這個字也只是當物件在處理),OOP 裡的type在OO裡是物件,但在OOP裡基本上不把type視為物件。

>而你一直把內容一樣的 class 視為可互轉
不只是內容一樣的 class互轉。
只要程式員認可的,都可以轉(C++提供最大的自由度讓程員可以轉換)。對錯就是程式員負責。
這才是C++的精神。

要繼續討論,請認真回2017/12/12 下午 05:23:19 和 2017/12/11 下午 03:34:51
尤其是2017/12/11 下午 03:34:51有一段通訊的例子,不能用dynamuc_cast(用了也不會過),那要如何撰寫這種需求的程式?
不要碰到重點就歪到別的議題,尤其是拿扭曲別人的話來爭論。
如果,只是這樣,無需再討論下去。我不是來吵架的。
作者 : cxxlman(CxxlMan) C++優秀好手貼文超過1000則
[ 貼文 1003 | 人氣 3227 | 評價 1260 | 評價/貼文 1.26 | 送出評價 27 次 ] 
[ 給個讚 ]  [ 給個讚 ]  [ 回應本文 ]  [ 發表新文 ]  [ 回上頁 ] [ 回討論區列表 ] [ 回知識入口 ]
2017/12/13 下午 03:16:45
好吧,改到你滿意

>>請先回頭看,是你要用來證明 dynamuc_cast 不安全,
>>我只是認為這證明無效
>蛤!?那裡?
>你不是一直堅持,dynamic_cast通不過就不能用static_cast取代,用static_cast就是亂搞嗎?

正解

>什麼時候變成我說dynamic_cast不安全?
>

你絕對有說,引給你看

>2017/11/27 下午 10:27:01
>好吧,我改寫一下程式,或許較能澄清
>if (dynamic_cast<Derived4*>(clone_pBase4))
> cout << '安全轉型' << endl;
>else
> cout << '不安全轉型' << endl;
>
>注意: '(不)安全' 不是 '(不)完全'
>所謂'不安全'只是沒辦法保證安全,並不是說轉型一定是錯的。
>如果程式員確定轉型是對的,可以用static_cast來強制轉型。

>我說的是:dynamic_cast 可以通過表示( 相對來說)是安全的,但不表示不通過就是有錯。

RTTI 概念很𥶆單也很確實,有就是有,沒有就是沒有,對就是對,錯就是錯
不通過就是沒有,不通過就是錯,不能用 static_cast 來證明 "不表示不通過就是有錯"

為什麼你我認知會有差異,因𠉎沒理解 oop 重視的是 type,不是內容,
而你一直把內容一樣的 class 視為可互轉的
作者 : cxxlman(CxxlMan) C++優秀好手貼文超過1000則
[ 貼文 1003 | 人氣 3227 | 評價 1260 | 評價/貼文 1.26 | 送出評價 27 次 ] 
[ 給個讚 ]  [ 給個讚 ]  [ 回應本文 ]  [ 發表新文 ]  [ 回上頁 ] [ 回討論區列表 ] [ 回知識入口 ]
2017/12/13 下午 03:26:23

>>RTTI 概念很𥶆單也很確實,有就是有,沒有就是沒有,對就是對,錯就是錯
>文件講的是'不安全'還是'錯'?兩個的差異,你了解嗎?
>如果照你這麼解釋,2017/12/12 下午 05:23:19我貼的轉換3.~轉換5.都不可能在 dynamuc_cast 通過。
>std lib和MFC class都有很多例子,你認為它們都是亂搞?

那關 dynamuc_cast 什麼事

>
>>不通過就是沒有,不通過就是錯,不能用 static_cast 來證明 '不表示不通過就是有錯'
>還是胡亂解釋我的話了!我說的是使用static_cast轉型,對錯由程式員自己負責,沒有要證明dynamuc_cast什麼。
>至於dynamuc_cast不通過就表示編譯無法確認轉型是安全而己,不表示不通過就是有錯。這是對安不安全的解釋,干static_cast何事?

dynamuc_cast 不通過表示物件含其成份,不關 static_cast 的事

>
>>為什麼你我認知會有差異,因𠉎沒理解 oo 重視的是 type,不是內容,
>type是OOP的東西,不是OO(提示很多次)。OO只談物件(就算提到type這個字也只是當物件在處理),OOP 裡的type在OO裡是物件,但在OOP裡基本上不把type視為物件。

已順你意了

>
>>而你一直把內容一樣的 class 視為可互轉
>不只是內容一樣的 class互轉。
>只要程式員認可的,都可以轉(C++提供最大的自由度讓程員可以轉換)。對錯就是程式員負責。
>這才是C++的精神。

那是 C 的棈神,你一定會說 CPP 你能這樣做,所以我才用 oo 而不用 oop

>
>要繼續討論,請認真回2017/12/12 下午 05:23:19 和 2017/12/11 下午 03:34:51
>尤其是2017/12/11 下午 03:34:51有一段通訊的例子,不能用dynamuc_cast(用了也不會過),那要如何撰寫這種需求的程式?
>不要碰到重點就歪到別的議題,尤其是拿扭曲別人的話來爭論。
>如果,只是這樣,無需再討論下去。我不是來吵架的。

那根本不關 dynamuc_cast 的事,dynamuc_cast 幹嘛去管通訊的事情
作者 : cxxlman(CxxlMan) C++優秀好手貼文超過1000則
[ 貼文 1003 | 人氣 3227 | 評價 1260 | 評價/貼文 1.26 | 送出評價 27 次 ] 
[ 給個讚 ]  [ 給個讚 ]  [ 回應本文 ]  [ 發表新文 ]  [ 回上頁 ] [ 回討論區列表 ] [ 回知識入口 ]
2017/12/13 下午 03:28:20

>>
>>>不通過就是沒有,不通過就是錯,不能用 static_cast 來證明 '不表示不通過就是有錯'
>>還是胡亂解釋我的話了!我說的是使用static_cast轉型,對錯由程式員自己負責,沒有要證明dynamuc_cast什麼。
>>至於dynamuc_cast不通過就表示編譯無法確認轉型是安全而己,不表示不通過就是有錯。這是對安不安全的解釋,干static_cast何事?
>
>dynamuc_cast 不通過表示物件含其成份,不關 static_cast 的事
>

這裡少打一個字

dynamuc_cast 不通過表示物件不含其成份,不關 static_cast 的事
作者 : cxxlman(CxxlMan) C++優秀好手貼文超過1000則
[ 貼文 1003 | 人氣 3227 | 評價 1260 | 評價/貼文 1.26 | 送出評價 27 次 ] 
[ 給個讚 ]  [ 給個讚 ]  [ 回應本文 ]  [ 發表新文 ]  [ 回上頁 ] [ 回討論區列表 ] [ 回知識入口 ]
2017/12/13 下午 03:36:13

>
>>
>>>而你一直把內容一樣的 class 視為可互轉
>>不只是內容一樣的 class互轉。
>>只要程式員認可的,都可以轉(C++提供最大的自由度讓程員可以轉換)。對錯就是程式員負責。
>>這才是C++的精神。
>
>那是 C 的棈神,你一定會說 CPP 你能這樣做,所以我才用 oo 而不用 oop

那是 C 的棈神,你一定會說 CPP 也能這樣做,所以我才用 oo 而不用 oop
作者 : sunyear(coco) VC++卓越專家C++頂尖高手貼文超過2000則
[ 貼文 2421 | 人氣 1485 | 評價 6060 | 評價/貼文 2.5 | 送出評價 5 次 ] 
[ 給個讚 ]  [ 給個讚 ]  [ 回應本文 ]  [ 發表新文 ]  [ 回上頁 ] [ 回討論區列表 ] [ 回知識入口 ]
2017/12/13 下午 03:39:42
S>什麼時候變成我說dynamic_cast不安全?
C>你絕對有說,引給你看

>2017/11/27 下午 10:27:01
S>好吧,我改寫一下程式,或許較能澄清
S>if (dynamic_cast<Derived4*>(clone_pBase4))
S> cout << '安全轉型' << endl;
S>else
S> cout << '不安全轉型' << endl;
S>
S>注意: '(不)安全' 不是 '(不)完全'
S>所謂'不安全'只是沒辦法保證安全,並不是說轉型一定是錯的。
S>如果程式員確定轉型是對的,可以用static_cast來強制轉型。
還是看不出我有說"dynamic_cast不安全"。
交通部(dynamic_cast)說馬路如虎口 (不安全轉型,因為不確定可以轉)。並不是說行人不能上馬路(不是不能轉)。行人上馬路就是自身安全自己顧(要轉就自負安全)。
終究你要把 '不安全轉型'等同於"錯的" 推向一定不能轉型,視為定律,那我沒辦法,但不要一直扭曲別人的意思。
不要再歪到無謂的文意之爭吧。
重點再貼一次:
要繼續討論,請認真回2017/12/12 下午 05:23:19 和 2017/12/11 下午 03:34:51
尤其是2017/12/11 下午 03:34:51有一段通訊的例子,不能用dynamuc_cast(用了也不會過),那要如何撰寫這種需求的程式?

作者 : cxxlman(CxxlMan) C++優秀好手貼文超過1000則
[ 貼文 1003 | 人氣 3227 | 評價 1260 | 評價/貼文 1.26 | 送出評價 27 次 ] 
[ 給個讚 ]  [ 給個讚 ]  [ 回應本文 ]  [ 發表新文 ]  [ 回上頁 ] [ 回討論區列表 ] [ 回知識入口 ]
2017/12/13 下午 03:59:31

>S>什麼時候變成我說dynamic_cast不安全?
>C>你絕對有說,引給你看
>
>>2017/11/27 下午 10:27:01
>S>好吧,我改寫一下程式,或許較能澄清
>S>if (dynamic_cast<Derived4*>(clone_pBase4))
>S> cout << '安全轉型' << endl;
>S>else
>S> cout << '不安全轉型' << endl;
>S>
>S>注意: '(不)安全' 不是 '(不)完全'
>S>所謂'不安全'只是沒辦法保證安全,並不是說轉型一定是錯的。
>S>如果程式員確定轉型是對的,可以用static_cast來強制轉型。
>還是看不出我有說'dynamic_cast不安全'。
>交通部(dynamic_cast)說馬路如虎口 (不安全轉型,因為不確定可以轉)。並不是說行人不能上馬路(不是不能轉)。行人上馬路就是自身安全自己顧(要轉就自負安全)。
>終究你要把 '不安全轉型'等同於'錯的' 推向一定不能轉型,視為定律,那我沒辦法,但不要一直扭曲別人的意思。
>不要再歪到無謂的文意之爭吧。
>重點再貼一次:
>要繼續討論,請認真回2017/12/12 下午 05:23:19 和 2017/12/11 下午 03:34:51
>尤其是2017/12/11 下午 03:34:51有一段通訊的例子,不能用dynamuc_cast(用了也不會過),那要如何撰寫這種需求的程式?

dynamic_cast 不能通過是 dynamic_cast 的事, static_cast 可以通過是 static_cast 的事,不能因 static_cast 可以通過就說 dynamic_cast 不能通過是"不安全" ,dynamic_cast 不能通過就是不能通過,沒有可質疑的
作者 : sunyear(coco) VC++卓越專家C++頂尖高手貼文超過2000則
[ 貼文 2421 | 人氣 1485 | 評價 6060 | 評價/貼文 2.5 | 送出評價 5 次 ] 
[ 給個讚 ]  [ 給個讚 ]  [ 回應本文 ]  [ 發表新文 ]  [ 回上頁 ] [ 回討論區列表 ] [ 回知識入口 ]
2017/12/13 下午 05:38:51
>dynamic_cast 不能通過是 dynamic_cast 的事, static_cast 可以通過是 static_cast 的事,
正確!不是一直跟你說dynamic_cast 不通過是它的事,不能因為dynamic_cast 不通過就說static_cast 也不能用嗎?

>不能因 static_cast 可以通過就說 dynamic_cast 不能通過是"不安全" ,
從來沒有這種說法!如果不能正確解讀別人的意思,就直接引文就好,不要亂詮釋。
所謂"不安全"是就dynamic_cast的檢查條件,認為不安全(未必是不能轉),這干static_cast 何事?
同樣的,程式員認為可以自負程式對錯,使用static_cast又干dynamic_cast何事?

要繼續討論,請認真回2017/12/12 下午 05:23:19 和 2017/12/11 下午 03:34:51
尤其是2017/12/11 下午 03:34:51有一段通訊的例子,不能用dynamuc_cast(用了也不會過),那要如何撰寫這種需求的程式?

那兩篇貼文我花了些時間寫的,目的是要討論問題本質,不是要你一直曲解文字,然後我拚命在澄清。所以
要繼續討論,請認真回2017/12/12 下午 05:23:19 和 2017/12/11 下午 03:34:51
尤其是2017/12/11 下午 03:34:51有一段通訊的例子,不能用dynamuc_cast(用了也不會過),那要如何撰寫這種需求的程式?
作者 : cxxlman(CxxlMan) C++優秀好手貼文超過1000則
[ 貼文 1003 | 人氣 3227 | 評價 1260 | 評價/貼文 1.26 | 送出評價 27 次 ] 
[ 給個讚 ]  [ 給個讚 ]  [ 回應本文 ]  [ 發表新文 ]  [ 回上頁 ] [ 回討論區列表 ] [ 回知識入口 ]
2017/12/13 下午 06:27:34

>>不能因 static_cast 可以通過就說 dynamic_cast 不能通過是'不安全' ,
>從來沒有這種說法!如果不能正確解讀別人的意思,就直接引文就好,不要亂詮釋。
>所謂'不安全'是就dynamic_cast的檢查條件,認為不安全(未必是不能轉),這干static_cast 何事?
>同樣的,程式員認為可以自負程式對錯,使用static_cast又干dynamic_cast何事?

你是說 clone() 得到的分身,dynamic_cast 檢查不合格,只要內容不變就可以用 static_cast 強製轉型???
我只聽說 typeid 檢查通過,才能用 static_cast 轉型

內容一樣可以用 static_cast 是 C 的概念,不能用在 C++



作者 : sunyear(coco) VC++卓越專家C++頂尖高手貼文超過2000則
[ 貼文 2421 | 人氣 1485 | 評價 6060 | 評價/貼文 2.5 | 送出評價 5 次 ] 
[ 給個讚 ]  [ 給個讚 ]  [ 回應本文 ]  [ 發表新文 ]  [ 回上頁 ] [ 回討論區列表 ] [ 回知識入口 ]
2017/12/13 下午 07:08:18
>我只聽說 typeid 檢查通過,才能用 static_cast 轉型
那裡聽說的?
能不能轉型,程式員才有主控權。怎麼轉,C++提供太多管道和方式(或語法)。
至於轉了,發生什麼事,那是程式員的責任。

>內容一樣可以用 static_cast 是 C 的概念,不能用在 C++
static_cast是C++才有的,目的也不是為了和C相容(C++保留C 原本的casting operator才是為了相容)
static_cast是有類別繼承(血緣關係)檢查的,這也是C沒有的。
一樣的物件可以在不同的操作類別轉換,是程式員認為沒問題後去轉換的(自負責任)。不是文件這麼說。
至於怎麼轉(用C++提供的那一種轉換方式),那也是程式員的選擇。
什麼型態的物件可以轉什麼型態,只有程式員可以掌握。C++只能提供有限的安全檢查,不能告訴你"一定不能轉"
static_cast的適用時機,一般的說法就是:基類別物件轉換至衍生類別物件。(這合於static_cast的檢查條件)
C++本來就不是安全導向的語言,一個可以直接杂取記憶體的語言,如何安全法?
C++它是提供彈性導向(限制最少)的語言。這是它的優點也是它的缺點。

請認真回2017/12/12 下午 05:23:19 和 2017/12/11 下午 03:34:51
尤其是2017/12/11 下午 03:34:51有一段通訊的例子,不能用dynamuc_cast(用了也不會過),那要如何撰寫這種需求的程式?
作者 : cxxlman(CxxlMan) C++優秀好手貼文超過1000則
[ 貼文 1003 | 人氣 3227 | 評價 1260 | 評價/貼文 1.26 | 送出評價 27 次 ] 
[ 給個讚 ]  [ 給個讚 ]  [ 回應本文 ]  [ 發表新文 ]  [ 回上頁 ] [ 回討論區列表 ] [ 回知識入口 ]
2017/12/13 下午 07:49:31

>>我只聽說 typeid 檢查通過,才能用 static_cast 轉型
>那裡聽說的?
>能不能轉型,程式員才有主控權。怎麼轉,C++提供太多管道和方式(或語法)。
>至於轉了,發生什麼事,那是程式員的責任。

因為只有 Base,它可以是任何 Derived 的 Base,程式員要怎麼負責任,若不用 dynamic_cast 轉,
就只可在 Base 這個層面做交流

>>內容一樣可以用 static_cast 是 C 的概念,不能用在 C++
>static_cast是C++才有的,目的也不是為了和C相容(C++保留C 原本的casting operator才是為了相容)

我是說內容一樣用不同的樣貌去使用這個概念

>static_cast是有類別繼承(血緣關係)檢查的,這也是C沒有的。
>一樣的物件可以在不同的操作類別轉換,是程式員認為沒問題後去轉換的(自負責任)。不是文件這麼說。

如果不做 classid 檢查做正確的 downcast 轉型,就只能做假轉型,因不知 Derived 是誰,而這
個假轉型就只能操作到 Base 這個層級的資料,那就用 Base 就好了,不然唯一的理由就是為 Base 增加操作功能函數

>至於怎麼轉(用C++提供的那一種轉換方式),那也是程式員的選擇。
>什麼型態的物件可以轉什麼型態,只有程式員可以掌握。C++只能提供有限的安全檢查,不能告訴你'一定不能轉'


>static_cast的適用時機,一般的說法就是:基類別物件轉換至衍生類別物件。(這合於static_cast的檢查條件)
>C++本來就不是安全導向的語言,一個可以直接杂取記憶體的語言,如何安全法?
>C++它是提供彈性導向(限制最少)的語言。這是它的優點也是它的缺點。
>
>請認真回2017/12/12 下午 05:23:19 和 2017/12/11 下午 03:34:51
>尤其是2017/12/11 下午 03:34:51有一段通訊的例子,不能用dynamuc_cast(用了也不會過),那要如何撰寫這種需求的程式?

dynamuc_cast 只做一個物件含有的 class 的轉型,不負責需求設計,關 dynamuc_cast 屁事

而且我是用 dynamuc_cast 來檢查 clone() 的分身,是否含有和本尊的一樣的成份,以判斷 clone() 的設計是否有問題,
跟我扯什麼安全不安全
作者 : sunyear(coco) VC++卓越專家C++頂尖高手貼文超過2000則
[ 貼文 2421 | 人氣 1485 | 評價 6060 | 評價/貼文 2.5 | 送出評價 5 次 ] 
[ 給個讚 ]  [ 給個讚 ]  [ 回應本文 ]  [ 發表新文 ]  [ 回上頁 ] [ 回討論區列表 ] [ 回知識入口 ]
2017/12/13 下午 10:48:43
>因為只有 Base,它可以是任何 Derived 的 Base,程式員要怎麼負責任,
>若不用 dynamic_cast 轉,就只可在 Base 這個層面做交流
C++有那麼多的conversion方式,怎麼會只能在 Base 這個層面使用?
程式對錯,本來就是程式員要負責,不然要叫誰負責?

>我是說內容一樣用不同的樣貌去使用這個概念
你有看了2017/12/12 下午 05:23:19的貼文了嗎?
不同大小,沒血緣型態的物件都可以轉了,相同的類別物件換不同的操作為什麼不能轉?
老王開了一家小公司,買了一輛Altis當業務的交通車(交通車繼承Altis);
後來老王老婆說,想買輛Altis當家用車(家用車繼承Altis)出遊使用。
老王想說,交通車少用,而且家用的時間幾乎都在非上班時間。
就跟老婆說,就把公司做交通車的那輛Altis,在下班後當家用車可以一舉兩得,又省錢。
老婆堅持不市,說我要是家用車,公司那輛叫交通車,怎麼可以。腦筋一定要這麼死嗎?

>如果不做 classid 檢查做正確的 downcast 轉型,就只能做假轉型,因不知 Derived 是誰,
什麼叫假轉型?
有看過前面的例子嗎?
CString 類別物件都可以轉型成char *,它們之間有繼承關係嗎?
int 都可以轉型成float,他們型別不同,也沒血緣關係,為什麼可以轉?
你講的那種Downcast是集合觀之下,編譯器可以判斷轉型沒問題。
其它編譯器無法判斷,要轉就是程式員要知道自己在做什麼。不知道亂轉就是後果自負。
像int和float因是內建型別,所以編譯器知道它們是什麼東西,要互轉要依什麼規則去轉。
如果改成自訂類別的int和float,編譯器就不知它們是什麼東東東了。dynamic_cast 檢查也一定不會過。
但真的不能轉嗎?要轉就只有程式員知道怎麼轉,結果自負。
底下寫一個模擬int和float互轉的例子,CInt物件轉成CFloat,沒血緣(繼承)關係,轉換演算和內建型別int轉float一樣,有什麼不可以嗎?
struct CFloat;
struct CInt
{
int m_iInt;
CInt(int i):m_iInt(i){}
operator float() {return (float)m_iInt;}
const CInt& operator =(int i) {
m_iint = i;
return *this;
}
virtual ~CInt() {}
};

struct CFloat
{
float m_fFloat;
CFloat(float f) :m_fFloat(f){}
operator int() {return (int)m_fFloat;}
const CFloat& operator =(float f) {
m_ffloat = f;
return *this;
}
virtual ~CFloat() {}
};


int main()
{
Cint iObj(5);
Cfloat fObj((float)1.23);

Cint *piObj= dynamic_cast<Cint *>(&fObj);//dynamic_cast得null, 失敗!
fObj = (float)iObj;//轉型OK

Cfloat *pfObj = dynamic_cast<Cfloat *>(&iObj);//dynamic_cast得null, 失敗!
iObj = (int)fObj;//轉型OK
return 0;
}


>而且我是用 dynamuc_cast 來檢查 clone() 的分身,是否含有和本尊的一樣的成份,
>以判斷 clone() 的設計是否有問題,跟我扯什麼安全不安全
沒什麼問題,就是用在我改的程式不跑而己。程式員該硬起來自己管轉型時,硬不土來而己。
作者 : sunyear(coco) VC++卓越專家C++頂尖高手貼文超過2000則
[ 貼文 2421 | 人氣 1485 | 評價 6060 | 評價/貼文 2.5 | 送出評價 5 次 ] 
[ 給個讚 ]  [ 給個讚 ]  [ 回應本文 ]  [ 發表新文 ]  [ 回上頁 ] [ 回討論區列表 ] [ 回知識入口 ]
2017/12/13 下午 10:59:56
CInt和CFloat加上下面這兩個operator就可以直接互轉:
struct CInt{
operator CFloat() {}
};

struct CFloat{
operator CInt() {}
};

>而且我是用 dynamuc_cast 來檢查 clone() 的分身,是否含有和本尊的一樣的成份,
>以判斷 clone() 的設計是否有問題,跟我扯什麼安全不安全
不是本尊不一定就不能轉,程式員知道怎麼轉就好。
安不安全回應你認為dynamic_cast返回null就是複製不完全。
dynamic_cast返回null是表示不安全(不代表不能轉)。不是複製不完全。
作者 : cxxlman(CxxlMan) C++優秀好手貼文超過1000則
[ 貼文 1003 | 人氣 3227 | 評價 1260 | 評價/貼文 1.26 | 送出評價 27 次 ] 
[ 給個讚 ]  [ 給個讚 ]  [ 回應本文 ]  [ 發表新文 ]  [ 回上頁 ] [ 回討論區列表 ] [ 回知識入口 ]
2017/12/14 上午 11:25:10

>>而且我是用 dynamuc_cast 來檢查 clone() 的分身,是否含有和本尊的一樣的成份,
>>以判斷 clone() 的設計是否有問題,跟我扯什麼安全不安全
>不是本尊不一定就不能轉,程式員知道怎麼轉就好。
>安不安全回應你認為dynamic_cast返回null就是複製不完全。
>dynamic_cast返回null是表示不安全(不代表不能轉)。不是複製不完全。

我是用 dynamic_cast 來檢驗 clone() 產生的分身和本尊是否一致,以判斷 clone() 是不是設計正確,
你跟我扯了老半天也搞不清楚你對這樣的檢驗方式是持什麼態度,所以我現在明確問你,你只要回答以
下重貼的檢驗方式有沒有效,是否足可判斷 clone() 的正確性,這樣就好

#include <iostream>
#include <string>

using namespace std;

class Base
{
public:
 virtual Base *Clone()
 {
  return new Base(*this);
 }
};

class Derived3 :public Base
{
public:
 virtual Base *Clone() override 
 {
  return new Derived3(*this);
 }
};

class Derived4 :public Base
{
public:
};

int main()
{
 Base *pBase3 = new Derived3; // 本尊
 Base *clone_pBase3 = pBase3->Clone(); // 分身

 cout << "Derived3 本尊:";
 if (dynamic_cast<Derived3*>(pBase3))
  cout << "完全" << endl;
 else
  cout << "不完全" << endl;

 cout << "Derived3 分身:";
 if (dynamic_cast<Derived3*>(clone_pBase3))
  cout << "完全" << endl;
 else
  cout << "不完全" << endl;

 cout << endl;

 Base *pBase4 = new Derived4; // 本尊
 Base *clone_pBase4 = pBase4->Clone(); // 分身

 cout << "Derived4 本尊:";
 if (dynamic_cast<Derived4*>(pBase4))
  cout << "完全" << endl;
 else
  cout << "不完全" << endl;

 cout << "Derived4 分身:";
 if (dynamic_cast<Derived4*>(clone_pBase4))
  cout << "完全" << endl;
 else
  cout << "不完全" << endl;

 return 0;
}

執行結果:
Derived3 本尊:完全
Derived3 分身:完全

Derived4 本尊:完全
Derived4 分身:不完全
請按任意鍵繼續 . . .



作者 : sunyear(coco) VC++卓越專家C++頂尖高手貼文超過2000則
[ 貼文 2421 | 人氣 1485 | 評價 6060 | 評價/貼文 2.5 | 送出評價 5 次 ] 
[ 給個讚 ]  [ 給個讚 ]  [ 回應本文 ]  [ 發表新文 ]  [ 回上頁 ] [ 回討論區列表 ] [ 回知識入口 ]
2017/12/14 下午 02:19:44
>我是用 dynamic_cast 來檢驗 clone() 產生的分身和本尊是否一致,以判斷 clone() 是不是設計正確,
>你跟我扯了老半天也搞不清楚你對這樣的檢驗方式是持什麼態度,所以我現在明確問你,你只要回答以
>下重貼的檢驗方式有沒有效,是否足可判斷 clone() 的正確性,這樣就好
同樣的問題都不知回多少次了,再回有什麼意義嗎?
dynamic_cast只是檢驗繼承關係,或說轉換目標和來源的原生類別關係。和本尊或複製是否完全根本沒關係。
你要用dynamic_cast沒人禁止你,但用dynamic_cast不能工作,你要堅持用不能work的方式,由你。
但人家告訴你可以static_cast解決,你說別人不能用static_cast才是原爭議所在吧?
還是重覆的程式,還需回答嗎?
OOP的繼承主要就是能延用基類別,你那程式本身在OOP觀點就有很大的問題,卻要人家解決的方法要多OOP。
別人解決方式本來就是OOP允計的轉換,老用什麼精神,完不完全,聽說,來搪塞。

請認真回2017/12/12 下午 05:23:19 和 2017/12/11 下午 03:34:51
尤其是2017/12/11 下午 03:34:51有一段通訊的例子,不能用dynamuc_cast(用了也不會過),那要如何撰寫這種需求的程式?

不能理解這兩個貼文,就無法破除你那dynamic_cast至上權威的迷思。所以先認真回。

作者 : sunyear(coco) VC++卓越專家C++頂尖高手貼文超過2000則
[ 貼文 2421 | 人氣 1485 | 評價 6060 | 評價/貼文 2.5 | 送出評價 5 次 ] 
[ 給個讚 ]  [ 給個讚 ]  [ 回應本文 ]  [ 發表新文 ]  [ 回上頁 ] [ 回討論區列表 ] [ 回知識入口 ]
2017/12/14 下午 03:10:20
看這段msdn對static_cast的說明;
https://msdn.microsoft.com/zh-tw/library/c36yw7x9.aspx
static_cast 可以進行反向隱含轉換,而其結果會是未定義。 程式設計人員必須確認 static_cast 轉換的結果是否安全。
此行為也適用於類別類型以外的類型。 例如,您可以使用 static_cast 將 int 轉換為 char。 不過,產生的 char 的位元可能不足以表示整個 int 值。 同樣地,程式設計人員必須確認 static_cast 轉換的結果是否安全。
static_cast 運算子也可用於執行所有隱含轉換,包括標準轉換和使用者定義的轉換。

注意後一句:包括...使用者定義的轉換。
想想什麼是使用者定義的轉換,那絕對不是你那本尊轉換的範圍吧!
還弄不清程式員如何負責,這一段話也是一直提示。
結論就是,物件型別轉換,沒什麼絕對不可以,就是程式員主控,程式安全自己負責。


請認真回2017/12/12 下午 05:23:19 和 2017/12/11 下午 03:34:51
尤其是2017/12/11 下午 03:34:51有一段通訊的例子,不能用dynamuc_cast(用了也不會過),那要如何撰寫這種需求的程式?
作者 : cxxlman(CxxlMan) C++優秀好手貼文超過1000則
[ 貼文 1003 | 人氣 3227 | 評價 1260 | 評價/貼文 1.26 | 送出評價 27 次 ] 
[ 給個讚 ]  [ 給個讚 ]  [ 回應本文 ]  [ 發表新文 ]  [ 回上頁 ] [ 回討論區列表 ] [ 回知識入口 ]
2017/12/15 下午 12:24:22

>>我是用 dynamic_cast 來檢驗 clone() 產生的分身和本尊是否一致,以判斷 clone() 是不是設計正確,
>>你跟我扯了老半天也搞不清楚你對這樣的檢驗方式是持什麼態度,所以我現在明確問你,你只要回答以
>>下重貼的檢驗方式有沒有效,是否足可判斷 clone() 的正確性,這樣就好
>同樣的問題都不知回多少次了,再回有什麼意義嗎?

那你再回答我,你說的那些是用來證明用的嗎,若不是用來證明,那麼有關係嗎,若不是用來證明又沒關係,就不用在這板討論了

以下若你確定和本板無關,建議你另開一個板討論

#include <iostream>
using namespace std;

class Base
{
public:
 double m_doubleValue;
 virtual ~Base(){}
};

class Derived1:public Base
{
 long m_Value;
public:
};

class Fake1 :public Base
{
public:
 void Set(double V)
 {
  m_doubleValue = V;
 }
};

void Set(Base *pBase, double V)
{
 pBase->m_doubleValue = V;
}

int main()
{
 Base *pBase = new Derived1;

 // 這樣還算情有可原,當作是為 Base 機動的增加功能函數
 Fake1 *pFake1 = static_cast<Fake1 *>(pBase);
 // 這兩個等價
 pFake1->Set(12);
 Set(pBase, 12);

 // 這是 pBase 所要的,因同一個物件的介面
 Derived1 *pDerived1 = dynamic_cast<Derived1*>(pBase);

 return 0;
}

當我拿到 pBase 時,我是把它當作是 pDerived1,拿到一個 Base 只當它是一個 Derived,因是同一個物件

而一個 pBase 可以有無數個 pFake1,因一個 Base 可以轉變成無數種 Fake,
也可以丟給無數種的函數,只要這些函數接受 Base,
Fake 就和那些函數一樣,只是一種功能應用設計,不是物件的一部份

作者 : sunyear(coco) VC++卓越專家C++頂尖高手貼文超過2000則
[ 貼文 2421 | 人氣 1485 | 評價 6060 | 評價/貼文 2.5 | 送出評價 5 次 ] 
[ 給個讚 ]  [ 給個讚 ]  [ 回應本文 ]  [ 發表新文 ]  [ 回上頁 ] [ 回討論區列表 ] [ 回知識入口 ]
2017/12/15 下午 05:52:56
>那你再回答我,你說的那些是用來證明用的嗎,若不是用來證明,那麼有關係嗎
你一直在模糊重點。
爭議是從2017/11/7 下午 09:49:30開始的,請回頭看看爭議點是什麼。
我2017/12/12 下午 05:23:19 和 2017/12/11 下午 03:34:51兩篇貼文,就是在說明static_cast使用的時機和合法性。

你只要理解我Coordinate是可以這樣寫,那麼2017/11/22 上午 01:20:31貼文裡的Derived3,Derived4就可以那樣寫。
Derived3,Derived4 OK那就破解:一定要覆寫clon的必要性。
所以就就回到原問題:如果衍生類別覺得沒必要覆寫函式,為何一定要被強迫覆寫函式。(這違反OOP 繼承複用的原則)

若還有問題,請再仔細參考2017/12/12 下午 05:23:19 和 2017/12/11 下午 03:34:51兩篇貼文和MSDN對static_cast的說明。
若沒問題就回到"如果衍生類別覺得沒必要覆寫函式,為何一定要被強迫覆寫函式。"這個癥結點上來。
其它一直扭曲別人意思,再讓別一窮於澄清的討論,就可免了。



作者 : cxxlman(CxxlMan) C++優秀好手貼文超過1000則
[ 貼文 1003 | 人氣 3227 | 評價 1260 | 評價/貼文 1.26 | 送出評價 27 次 ] 
[ 給個讚 ]  [ 給個讚 ]  [ 回應本文 ]  [ 發表新文 ]  [ 回上頁 ] [ 回討論區列表 ] [ 回知識入口 ]
2017/12/16 上午 11:06:02

>>那你再回答我,你說的那些是用來證明用的嗎,若不是用來證明,那麼有關係嗎
>你一直在模糊重點。
>爭議是從2017/11/7 下午 09:49:30開始的,請回頭看看爭議點是什麼。
>我2017/12/12 下午 05:23:19 和 2017/12/11 下午 03:34:51兩篇貼文,就是在說明static_cast使用的時機和合法性。
>
>你只要理解我Coordinate是可以這樣寫,那麼2017/11/22 上午 01:20:31貼文裡的Derived3,Derived4就可以那樣寫。
>Derived3,Derived4 OK那就破解:一定要覆寫clon的必要性。
>所以就就回到原問題:如果衍生類別覺得沒必要覆寫函式,為何一定要被強迫覆寫函式。(這違反OOP 繼承複用的原則)
>
>若還有問題,請再仔細參考2017/12/12 下午 05:23:19 和 2017/12/11 下午 03:34:51兩篇貼文和MSDN對static_cast的說明。
>若沒問題就回到'如果衍生類別覺得沒必要覆寫函式,為何一定要被強迫覆寫函式。'這個癥結點上來。
>其它一直扭曲別人意思,再讓別一窮於澄清的討論,就可免了。

若不實作 Clone(),當你拿到 Base *pBase,這個 pBase 是無數種 Derived 的 Base,你怎麼知道要怎麼 clone



作者 : sunyear(coco) VC++卓越專家C++頂尖高手貼文超過2000則
[ 貼文 2421 | 人氣 1485 | 評價 6060 | 評價/貼文 2.5 | 送出評價 5 次 ] 
[ 給個讚 ]  [ 給個讚 ]  [ 回應本文 ]  [ 發表新文 ]  [ 回上頁 ] [ 回討論區列表 ] [ 回知識入口 ]
2017/12/16 下午 01:58:08
>若不實作 Clone(),當你拿到 Base *pBase,這個 pBase 是無數種 Derived 的 Base,你怎麼知道要怎麼 clone
當Derived3決定把clone交給Base時,就表示全權交給Base clone(程式員知道在做什麼);
若DerivedX認為詃有自己的clone就會實做自己的clone。
結果無數種Derived的Base都會呼叫各自Derived所設想的clone(不論是自己實作譞是交給Base)。
作者 : cxxlman(CxxlMan) C++優秀好手貼文超過1000則
[ 貼文 1003 | 人氣 3227 | 評價 1260 | 評價/貼文 1.26 | 送出評價 27 次 ] 
[ 給個讚 ]  [ 給個讚 ]  [ 回應本文 ]  [ 發表新文 ]  [ 回上頁 ] [ 回討論區列表 ] [ 回知識入口 ]
2017/12/16 下午 02:59:23

>>若不實作 Clone(),當你拿到 Base *pBase,這個 pBase 是無數種 Derived 的 Base,你怎麼知道要怎麼 clone
>當Derived3決定把clone交給Base時,就表示全權交給Base clone(程式員知道在做什麼);
>若DerivedX認為詃有自己的clone就會實做自己的clone。
>結果無數種Derived的Base都會呼叫各自Derived所設想的clone(不論是自己實作譞是交給Base)。

可是這樣有兩個問題

1. 若 Derived 沒實作 Clone(),本尊和分身會有不同成份,即 most derived 會不同,這樣是不是有違 Clone() 的本意
2. Derived 沒實作 Clone() 事實上是檢查不出來的,我用 dynamic_cast 做檢查是事先已知 pBase 的 Derived 是誰,若沒實作事實上分身也能正常執行,只是在錯誤中執行還渾然不知,所以強制實作是必要的
作者 : sunyear(coco) VC++卓越專家C++頂尖高手貼文超過2000則
[ 貼文 2421 | 人氣 1485 | 評價 6060 | 評價/貼文 2.5 | 送出評價 5 次 ] 
[ 給個讚 ]  [ 給個讚 ]  [ 回應本文 ]  [ 發表新文 ]  [ 回上頁 ] [ 回討論區列表 ] [ 回知識入口 ]
2017/12/16 下午 05:50:13
>1. 若 Derived 沒實作 Clone(),本尊和分身會有不同成份,即 most derived 會不同,這樣是不是有違 Clone() 的本意
不會,derived的成資料和Base不同,它就自己要做clone,沒人限制它不能實作自己的clone(但你的clone範例不能被derived呼叫,會有問題,但先不談這個)。
但少部份的derived資料若完全和Base一樣,即使只有百分之一,這百分之一為什麼要被強迫實作clone?這有違OOP繼承本意。

>2. Derived 沒實作 Clone() 事實上是檢查不出來的,我用 dynamic_cast 做檢查是事先已知 pBase 的 Derived 是誰,
>若沒實作事實上分身也能正常執行,只是在錯誤中執行還渾然不知,所以強制實作是必要的
如果不用強迫實作,能不能檢查出來有沒有實作就不是重點。
不懂"能正常執行,只是在錯誤中執行還渾然不知",能正常執行的程式,就是沒有錯誤,不是嗎?
我那Coordinate合乎C++規範,也沒有錯誤,你要檢查它什麼?
物件型態轉換,是否有錯,要是程式員自己要負責,不是靠編譯器檢查,更不是靠強迫實作clone。
難道有實作clone的derived就不會出錯嗎?
作者 : cxxlman(CxxlMan) C++優秀好手貼文超過1000則
[ 貼文 1003 | 人氣 3227 | 評價 1260 | 評價/貼文 1.26 | 送出評價 27 次 ] 
[ 給個讚 ]  [ 給個讚 ]  [ 回應本文 ]  [ 發表新文 ]  [ 回上頁 ] [ 回討論區列表 ] [ 回知識入口 ]
2017/12/17 上午 11:36:51
因為會有以下問題,所以須強制實作

#include <iostream>
using namespace std;

class Base
{
public:
 virtual ~Base() {}

 virtual void SayHello()
 {
  cout << "I am Base" << endl;
 }

 virtual Base* Clone()
 {
  return new Base;
 }
};

class Derived1 :public Base
{
public:
 virtual void SayHello()
 {
  cout << "I am Derived1" << endl;
 }

 virtual Base* Clone()
 {
  return new Derived1;
 }
};

class Derived2 :public Base
{
public:
 virtual void SayHello()
 {
  cout << "I am Derived2" << endl;
 }
};

int main()
{
 Base *pBase = new Derived1;
 Base *pCloneBase = pBase->Clone();
 pCloneBase->SayHello(); // I am Derived1

 pBase = new Derived2;
 pCloneBase = pBase->Clone();
 pCloneBase->SayHello(); // I am Base

 return 0;
}

問題在 Derived2 沒有實作 Clone()
作者 : sunyear(coco) VC++卓越專家C++頂尖高手貼文超過2000則
[ 貼文 2421 | 人氣 1485 | 評價 6060 | 評價/貼文 2.5 | 送出評價 5 次 ] 
[ 給個讚 ]  [ 給個讚 ]  [ 回應本文 ]  [ 發表新文 ]  [ 回上頁 ] [ 回討論區列表 ] [ 回知識入口 ]
2017/12/17 下午 04:53:13
>因為會有以下問題,所以須強制實作
>class Base
>{
>public:
> virtual ~Base() {}
> virtual void SayHello()
> {
>  cout << "I am Base" << endl;
> }
> virtual Base* Clone()
> {
>  return new Base;
> }
>};
>
>class Derived2 :public Base
>{
>public:
> virtual void SayHello()
> {
>  cout << "I am Derived2" << endl;
> }
>};

>int main()
>{
> pBase = new Derived2;
> pCloneBase = pBase->Clone();
> pCloneBase->SayHello(); // I am Base
>
> return 0;
>}
>
>問題在 Derived2 沒有實作 Clone()

終於是回到強制實作需求的問題了。
你這個例子,要達到SayHello()可以正確印出身份的目的,需要clone和SayHello都要強制實作才成,是不是?
clone的問題,前面談過了,SayHello的例子,在我來看,它只是在展示衍生類別沒有覆寫基類別虛擬函式時,呼叫的是基類別虛擬函式而己。
若是檢查衍生類別是否有覆寫芋個函式,其實用rtti檢查就可以知道。但這檢查的意義是什麼?至少不會跟需不需要強制實作有關係吧?
我還是用Derived3來說明。
你的Derived1例子有依你要求覆寫clone和SayHello,沒問題,是吧?
那我如果寫一個Derived3,一樣只是為了修飾Derived1,我並不想改變Derived1什麼,包括類別名稱。Derived3有沒有不覆寫clone和SayHello的選擇權?
我的意思是,設計Derived3時,程式員認為使用基類的clone和SayHello對Derived3沒什麼影響,也打算延用,為什麼一定要在Derived3覆寫它們?
兒子想借老爸的車子載女朋友去玩一天,老爸說:可以,但你要先把車子過戶給自己,用完再過戶回來還給我。
兒子子覺得好麻煩,回說:不過就借用個一天,就要辦過戶,有這麼嚴重嗎?
作者 : cxxlman(CxxlMan) C++優秀好手貼文超過1000則
[ 貼文 1003 | 人氣 3227 | 評價 1260 | 評價/貼文 1.26 | 送出評價 27 次 ] 
[ 給個讚 ]  [ 給個讚 ]  [ 回應本文 ]  [ 發表新文 ]  [ 回上頁 ] [ 回討論區列表 ] [ 回知識入口 ]
2017/12/17 下午 05:19:23


>我的意思是,設計Derived3時,程式員認為使用基類的clone和SayHello對Derived3沒什麼影響,也打算延用,為什麼一定要在Derived3覆寫它們?

因為只能從 Base 這個介面進行操作,Derived3 不覆寫 Clone(),在 pCloneBase 將不存在 Derived3
作者 : sunyear(coco) VC++卓越專家C++頂尖高手貼文超過2000則
[ 貼文 2421 | 人氣 1485 | 評價 6060 | 評價/貼文 2.5 | 送出評價 5 次 ] 
[ 給個讚 ]  [ 給個讚 ]  [ 回應本文 ]  [ 發表新文 ]  [ 回上頁 ] [ 回討論區列表 ] [ 回知識入口 ]
2017/12/17 下午 06:46:29
>因為只能從 Base 這個介面進行操作,Derived3 不覆寫 Clone(),在 pCloneBase 將不存在 Derived3
Derived3如果不在乎呢?在設計Derived3時就不打算在乎自己存不存在。如果Derived3有加了一個函式,需用時就暫時cast成成Derived3,就可以。
兒子要借老爸的車開,就暫時帶著老爸的行照就可以了,不需要法律強制要過戶(行照一定是要開車者的名子)。
作者 : cxxlman(CxxlMan) C++優秀好手貼文超過1000則
[ 貼文 1003 | 人氣 3227 | 評價 1260 | 評價/貼文 1.26 | 送出評價 27 次 ] 
[ 給個讚 ]  [ 給個讚 ]  [ 回應本文 ]  [ 發表新文 ]  [ 回上頁 ] [ 回討論區列表 ] [ 回知識入口 ]
2017/12/17 下午 06:46:58

>
>
>>我的意思是,設計Derived3時,程式員認為使用基類的clone和SayHello對Derived3沒什麼影響,也打算延用,為什麼一定要在Derived3覆寫它們?
>
>因為只能從 Base 這個介面進行操作,Derived3 不覆寫 Clone(),在 pCloneBase 將不存在

1. 你的 Derived3 沒影響,不代表別人的 Derived3 沒影響
2. 你的 Derived3 可用於 pBase,一樣可以用於 pCloneBase

權衡得失一定要強制實作 Clone()
作者 : sunyear(coco) VC++卓越專家C++頂尖高手貼文超過2000則
[ 貼文 2421 | 人氣 1485 | 評價 6060 | 評價/貼文 2.5 | 送出評價 5 次 ] 
[ 給個讚 ]  [ 給個讚 ]  [ 回應本文 ]  [ 發表新文 ]  [ 回上頁 ] [ 回討論區列表 ] [ 回知識入口 ]
2017/12/17 下午 08:40:16
>1. 你的 Derived3 沒影響,不代表別人的 Derived3 沒影響
對啊!
有影響的就自己覆寫Clone/SayHello;沒影響的就不用覆寫。
不是一直都這樣說的嗎?為什麼有影響的要強制沒影響的要覆寫?
若說,這樣比較安全。不覆寫基類函式會造成錯誤的程式,多如牛牛。
要照顧也照顧不完!(基本上,只要不是亂寫。所有覆寫函式成員的程式,若不覆寫幾乎都是有問題的)
該不該覆寫,就是程式員自己想,自己決定,自己負責。不是由基類別告訴他。結果連可以不覆寫的都要被強迫覆寫。

>2. 你的 Derived3 可用於 pBase,一樣可以用於 pCloneBase
>權衡得失一定要強制實作
不覆寫的結果,就是Derived3認為它可以延用基類物件和身份,也可以轉型到Derived3來操作(一般就是Derived3沒自己的資料員)
作者 : cxxlman(CxxlMan) C++優秀好手貼文超過1000則
[ 貼文 1003 | 人氣 3227 | 評價 1260 | 評價/貼文 1.26 | 送出評價 27 次 ] 
[ 給個讚 ]  [ 給個讚 ]  [ 回應本文 ]  [ 發表新文 ]  [ 回上頁 ] [ 回討論區列表 ] [ 回知識入口 ]
2017/12/17 下午 10:08:48
功力就在這了,絕不會讓瑕疵品有機會出現,買的安心,賣的也放心
作者 : sunyear(coco) VC++卓越專家C++頂尖高手貼文超過2000則
[ 貼文 2421 | 人氣 1485 | 評價 6060 | 評價/貼文 2.5 | 送出評價 5 次 ] 
[ 給個讚 ]  [ 給個讚 ]  [ 回應本文 ]  [ 發表新文 ]  [ 回上頁 ] [ 回討論區列表 ] [ 回知識入口 ]
2017/12/17 下午 10:41:04
>功力就在這了,絕不會讓瑕疵品有機會出現,買的安心,賣的也放心
那是不可能的事!
程式的優劣對錯五花八門。
如果程式員連該不該覆寫繼承下來的函式都搞不清楚,寫出來的程式會讓人放心?
繼承的精神就是程式碼可以複用或改寫,就算改寫大部的情況也會呼叫基類函式。
但有很多情況是,類別或函式成員是不希望再被繼承的,這就是c++1.1新增的final。
既要別人繼承又不給繼承者呼叫使用,是違反繼承精神的。像你的clone,一般clone不是只用預設的複製建構子就可以的。
還要自行管理一些類別資料成員的複製。這些成員若是在基類private,clone又不給衍生類別呼叫,程式幾乎是寫不下去的。
就算這些資料是在protected或public,要衍生類別的clone去負責基類的clone工作也是很怪的。
寫一個這麼怪的clone,又達不到說服人為什麼強制覆寫是必要的。
或許must有什麼case是有需要的(若有,我也有興趣知道),但clone絕不會是個must需要的好範例。
作者 : cxxlman(CxxlMan) C++優秀好手貼文超過1000則
[ 貼文 1003 | 人氣 3227 | 評價 1260 | 評價/貼文 1.26 | 送出評價 27 次 ] 
[ 給個讚 ]  [ 給個讚 ]  [ 回應本文 ]  [ 發表新文 ]  [ 回上頁 ] [ 回討論區列表 ] [ 回知識入口 ]
2017/12/17 下午 11:02:03
clone絕對是must需要的好範例,因為有 wiki 背書:

克隆(英语:Clone)在廣義上是指利用生物技术由は性生殖产生与原个体有完全相同基因组之后代的过程。

若克隆出來的分身和本尊不一樣,那才會讓人疑惑

作者 : cheg(cheg)
[ 貼文 99 | 人氣 125 | 評價 410 | 評價/貼文 4.14 | 送出評價 24 次 ] 
[ 給個讚 ]  [ 給個讚 ]  [ 回應本文 ]  [ 發表新文 ]  [ 回上頁 ] [ 回討論區列表 ] [ 回知識入口 ]
2017/12/18 上午 12:14:04
呃 clone 跟 accept 應該是適合 must 沒錯啦
這真的不用爭,一開始討論的就是這個
使用不同繼承架構是目前比較合理的解決方法
你用多重繼承來強制 must 其實有個問題
當你寫 PROTOTYPE_BASE_CRTP 的時候
其實就是你知道要改寫那些函數的時候了 : -)
  
作者 : cxxlman(CxxlMan) C++優秀好手貼文超過1000則
[ 貼文 1003 | 人氣 3227 | 評價 1260 | 評價/貼文 1.26 | 送出評價 27 次 ] 
[ 給個讚 ]  [ 給個讚 ]  [ 回應本文 ]  [ 發表新文 ]  [ 回上頁 ] [ 回討論區列表 ] [ 回知識入口 ]
2017/12/18 下午 01:09:29

>當你寫 PROTOTYPE_BASE_CRTP 的時候
>其實就是你知道要改寫那些函數的時候了 : -)

PROTOTYPE_BASE_CRTP 問題很多,也忘了有哪些問題了
現在是用 介面繼承+組合,但用這方法要有有力的物件管理做後盾
所以 must 是最簡單的方法

作者 : cheg(cheg)
[ 貼文 99 | 人氣 125 | 評價 410 | 評價/貼文 4.14 | 送出評價 24 次 ] 
[ 給個讚 ]  [ 給個讚 ]  [ 回應本文 ]  [ 發表新文 ]  [ 回上頁 ] [ 回討論區列表 ] [ 回知識入口 ]
2017/12/18 下午 02:43:03
>PROTOTYPE_BASE_CRTP 問題很多,也忘了有哪些問題了
>現在是用 介面繼承+組合,但用這方法要有有力的物件管理做後盾
>所以 must 是最簡單的方法
>
>

不論能否解決你說的 PROTOTYPE_BASE_CRTP 那些問題
它與 must 最根本的差別是,must 在設計基礎類別的時候就可以強制改寫
而它是在子類別繼承的時候去使用達到強制力,問題就在這裡,如果說你都
知道要呼叫這個巨集了,等於你知道目前這個類別有額外的責任 (改寫 clone 與 accept)
這跟 must 的初衷並不相符,所以並不能取代它,不過使用它還是有提醒作用
它等於是先立一個 tag,如果你有很多函數要改寫,不可能一次寫完,這個巨集還是可以啟到提醒的作用。
  
  
作者 : sunyear(coco) VC++卓越專家C++頂尖高手貼文超過2000則
[ 貼文 2421 | 人氣 1485 | 評價 6060 | 評價/貼文 2.5 | 送出評價 5 次 ] 
[ 給個讚 ]  [ 給個讚 ]  [ 回應本文 ]  [ 發表新文 ]  [ 回上頁 ] [ 回討論區列表 ] [ 回知識入口 ]
2017/12/18 下午 07:07:36
>clone絕對是must需要的好範例,因為有 wiki 背書:
>克隆(英语:Clone)在廣義上是指利用生物技术由は性生殖产生与原个体有完全相同基因组之后代的过程。
你或我都可以去編wiki^^,且不爭wiki的權威性。
你擴大解釋這句話的意思,也誤解我的意思。我從沒說clone不必複製完全相同的物件。
這句話的意是,複製的結果要和被複製者完全相同,但要達成這目的,沒說非得must不可。
有must,但程式員沒clone的觀念或程式寫錯,還是可能複製一個不完全相同的分身。
must若做為一個Hint或Warning很好。但變成強制就不好。理由就是我前面一直說的:
OOP 或 C++的物件,指的是資料(包括Vtable),不是類別裡面的函式。
如果繼承類別並沒有增加或改變任何資料,不管是父或子clone都是可以製做相同的物件,那為何不能交給父類別clone就好?
同是複製出一模一樣的物件!只要程式員知道在做什麼,有何不可?


作者 : cxxlman(CxxlMan) C++優秀好手貼文超過1000則
[ 貼文 1003 | 人氣 3227 | 評價 1260 | 評價/貼文 1.26 | 送出評價 27 次 ] 
[ 給個讚 ]  [ 給個讚 ]  [ 回應本文 ]  [ 發表新文 ]  [ 回上頁 ] [ 回討論區列表 ] [ 回知識入口 ]
2017/12/18 下午 07:11:44

>
>不論能否解決你說的 PROTOTYPE_BASE_CRTP 那些問題
>它與 must 最根本的差別是,must 在設計基礎類別的時候就可以強制改寫
>而它是在子類別繼承的時候去使用達到強制力,問題就在這裡,如果說你都
>知道要呼叫這個巨集了,等於你知道目前這個類別有額外的責任 (改寫 clone 與 accept)
>這跟 must 的初衷並不相符,所以並不能取代它,不過使用它還是有提醒作用
>它等於是先立一個 tag,如果你有很多函數要改寫,不可能一次寫完,這個巨集還是可以啟到提醒的作用。

因沒有 must 可用,所以想說沒魚蝦也好,只要能有個提醒輔助的就行了

作者 : cxxlman(CxxlMan) C++優秀好手貼文超過1000則
[ 貼文 1003 | 人氣 3227 | 評價 1260 | 評價/貼文 1.26 | 送出評價 27 次 ] 
[ 給個讚 ]  [ 給個讚 ]  [ 回應本文 ]  [ 發表新文 ]  [ 回上頁 ] [ 回討論區列表 ] [ 回知識入口 ]
2017/12/18 下午 09:31:48

>>clone絕對是must需要的好範例,因為有 wiki 背書:
>>克隆(英语:Clone)在廣義上是指利用生物技术由は性生殖产生与原个体有完全相同基因组之后代的过程。
>你或我都可以去編wiki^^,且不爭wiki的權威性。
>你擴大解釋這句話的意思,也誤解我的意思。我從沒說clone不必複製完全相同的物件。
>這句話的意是,複製的結果要和被複製者完全相同,但要達成這目的,沒說非得must不可。
>有must,但程式員沒clone的觀念或程式寫錯,還是可能複製一個不完全相同的分身。
>must若做為一個Hint或Warning很好。但變成強制就不好。理由就是我前面一直說的:
>OOP 或 C++的物件,指的是資料(包括Vtable),不是類別裡面的函式。
>如果繼承類別並沒有增加或改變任何資料,不管是父或子clone都是可以製做相同的物件,那為何不能交給父類別clone就好?
>同是複製出一模一樣的物件!只要程式員知道在做什麼,有何不可?

介面設計採用 Clone() 就要想辦法讓定義確實履行,這樣給使用端才算有交代,赴湯蹈火在所不辭,若做不到
就應該把 Clone() 拿掉,這樣才叫負責

所以 Clone() 只能用與不用,沒有妥協的餘地


作者 : sunyear(coco) VC++卓越專家C++頂尖高手貼文超過2000則
[ 貼文 2421 | 人氣 1485 | 評價 6060 | 評價/貼文 2.5 | 送出評價 5 次 ] 
[ 給個讚 ]  [ 給個讚 ]  [ 回應本文 ]  [ 發表新文 ]  [ 回上頁 ] [ 回討論區列表 ] [ 回知識入口 ]
主題發起人cxxlman註記此篇回應為最佳解答 2017/12/19 下午 01:51:41
>介面設計採用 Clone() 就要想辦法讓定義確實履行,這樣給使用端才算有交代,赴湯蹈火在所不辭,若做不到
>就應該把 Clone() 拿掉,這樣才叫負責所以 Clone() 只能用與不用,沒有妥協的餘地
"赴湯蹈火",有這麼嚴重嗎^^
這只是你的選擇,就我來看,只要衍生類別物件和基類是一樣的,呼叫基類別的Clone是沒問題的。
所以以Clone要說服我must的必要性,我很難認同。
但,真要提醒衍生類要實作自己的Clone,其實不必像前面寫的那麼複雜(接手的手光要看懂和使用就要費不少心力)。
像這樣就可以了:
class Base
{
public:
virtual Base *Clone()
{
assert(strcmp("class Base", typeid(*this).name())==0);//在debug階段,檢查衍生類別是否實作Clone
return new Base(*this);
}
};

class Derived1 :public Base
{
public:
virtual Base *Clone()
{
assert(strcmp("class Derived1",typeid(*this).name())==0);//在debug階段,檢查衍生類別是否實作Clone
return new Derived1(*this);
}

};
作者 : cxxlman(CxxlMan) C++優秀好手貼文超過1000則
[ 貼文 1003 | 人氣 3227 | 評價 1260 | 評價/貼文 1.26 | 送出評價 27 次 ] 
[ 給個讚 ]  [ 給個讚 ]  [ 回應本文 ]  [ 發表新文 ]  [ 回上頁 ] [ 回討論區列表 ] [ 回知識入口 ]
2017/12/19 下午 07:15:24
可行,記大功一件,不過要改一下

#include <string.h>
#include <assert.h>   /* assert */
#include <typeinfo>   /* assert */
#include <iostream>

using namespace std;

class Base
{
public:
 virtual Base *Clone()
 {
  Base *p = new Base(*this);
  assert(strcmp(typeid(*p).name(), typeid(*this).name()) == 0);//在debug階段,檢查衍生類別是否實作Clone
  return p;
 }
};

class Derived1 :public Base
{
public:
 virtual Base *Clone()
 {
  Base *p = new Derived1(*this);
  assert(strcmp(typeid(*p).name(), typeid(*this).name()) == 0);//在debug階段,檢查衍生類別是否實作Clone
  return p;
 }
};


int main()
{
 Base *pBase = new Derived1;
 Base *pCloneBase = pBase->Clone();

 return 0;
}


作者 : cxxlman(CxxlMan) C++優秀好手貼文超過1000則
[ 貼文 1003 | 人氣 3227 | 評價 1260 | 評價/貼文 1.26 | 送出評價 27 次 ] 
[ 給個讚 ]  [ 給個讚 ]  [ 回應本文 ]  [ 發表新文 ]  [ 回上頁 ] [ 回討論區列表 ] [ 回知識入口 ]
2017/12/19 下午 07:29:25
不過還得提醒接手者依樣畫葫蘆,還是提供 must 會比較簡單
作者 : sunyear(coco) VC++卓越專家C++頂尖高手貼文超過2000則
[ 貼文 2421 | 人氣 1485 | 評價 6060 | 評價/貼文 2.5 | 送出評價 5 次 ] 
[ 給個讚 ]  [ 給個讚 ]  [ 回應本文 ]  [ 發表新文 ]  [ 回上頁 ] [ 回討論區列表 ] [ 回知識入口 ]
2017/12/19 下午 08:24:49
>不過還得提醒接手者依樣畫葫蘆,還是提供 must 會比較簡單
基本上。我認為不會提供。第一,沒需要性,第二有違繼承原則。
關於第二點,原則是,基類可以決定本身是否可傳到下代,但不應限制下一代是否允許傳給下下一代。
(就算你不同意下代可以引用基類的Clone,但如果下代有其它的Clone做法,可以傳給下下代引用呢?)
就算C++應你要求給你must好了,下代要不聽話,寫成這樣,你的must又能耐何?(像我就可能不聽話,因為我認為可以延用基類clone)
class Derived1 :public Base
{
public:
virtual Base *Clone()
{return Base ::Clone();}
};

使用assert方式,除了表明不給子代繼承使用,也是一種提醒下代,;若沒有其它想法,就還是把assert加上去。
若原始基類設計,是期望(也僅能期望)Clone都是一樣的寫法,那使用成員函式模版給衍生類別使用,可避免漏寫assert。
這就能達到你的目的。但絡歸是期望,無法強制(連must都無法強制),子代要不聽話,做父親的不要想太多,以為管得了(不聽話有兩種,一種是亂搞,另一種是有自己的想法)。
另外一個建議,Clone寫成這樣,在使用時,若有衍生類別忘了自己的Clone,在編譯時就可檢知:
class Base
{
public:
virtual Base *Clone(){
return new Base(*this); }
};

class Derived1 :public Base
{
public:
virtual Derived1 *Clone(){
return new Derived1(*this);}

};

class Derived2 :public Base
{
//沒有實作Clone
};

int main()
{
Derived1 *pB1 = new Derived1;
Derived2 *pB2 = new Derived2;

Derived1 *pC1 = pB1->Clone();
Derived2 *pC2 = pB2->Clone();//在編譯階段就可檢知Derived2沒有實作Clone
}

使用assert檢查再加上covariant return types的幫忙,最後用template把Clone的實作包起來,應該聽話的小孩很難出軌了。
但小孩要不聽話,神仙難救(must也沒用)。
作者 : cxxlman(CxxlMan) C++優秀好手貼文超過1000則
[ 貼文 1003 | 人氣 3227 | 評價 1260 | 評價/貼文 1.26 | 送出評價 27 次 ] 
[ 給個讚 ]  [ 給個讚 ]  [ 回應本文 ]  [ 發表新文 ]  [ 回上頁 ] [ 回討論區列表 ] [ 回知識入口 ]
2017/12/20 上午 09:37:27


>int main()
>{
> Derived1 *pB1 = new Derived1;
> Derived2 *pB2 = new Derived2;
>
> Derived1 *pC1 = pB1->Clone();
> Derived2 *pC2 = pB2->Clone();//在編譯階段就可檢知Derived2沒有實作Clone
>}

由於你用法不當,導致觀點偏差,所以就一直在各說各法
你應該由 Base 的使用角度去考量,而不是由 Derived

Base *Fun(Base *pBase)
{
  return pBase->Clone();
}


>
>使用assert檢查再加上covariant return types的幫忙,最後用template把Clone的實作包起來,應該聽話的小孩很難出軌了。

靜態繼承我也用考慮過,不過那和 PROTOTYPE_BASE_CRTP 做法的效果相當

>但小孩要不聽話,神仙難救(must也沒用)。

assert 只在 Clone() 效果較好,若用在 Visitor 的 Accept() 中也要產生一個用不到只作為 assert 檢查的物件,你是否能接受
作者 : cxxlman(CxxlMan) C++優秀好手貼文超過1000則
[ 貼文 1003 | 人氣 3227 | 評價 1260 | 評價/貼文 1.26 | 送出評價 27 次 ] 
[ 給個讚 ]  [ 給個讚 ]  [ 回應本文 ]  [ 發表新文 ]  [ 回上頁 ] [ 回討論區列表 ] [ 回知識入口 ]
2017/12/20 下午 01:59:02

>
>>但小孩要不聽話,神仙難救(must也沒用)。
>
>assert 只在 Clone() 效果較好,若用在 Visitor 的 Accept() 中也要產生一個用不到只作為 assert 檢查的物件,你是否能接受

不須產生物件,這樣也可以

assert(strcmp(typeid(Base).name(), typeid(*this).name()) == 0);//在debug階段,檢查衍生類別是否實作Clone

作者 : sunyear(coco) VC++卓越專家C++頂尖高手貼文超過2000則
[ 貼文 2421 | 人氣 1485 | 評價 6060 | 評價/貼文 2.5 | 送出評價 5 次 ] 
[ 給個讚 ]  [ 給個讚 ]  [ 回應本文 ]  [ 發表新文 ]  [ 回上頁 ] [ 回討論區列表 ] [ 回知識入口 ]
2017/12/20 下午 04:36:46
>int main()
>>{
>> Derived1 *pB1 = new Derived1;
>> Derived2 *pB2 = new Derived2;
>>
>> Derived1 *pC1 = pB1->Clone();
>> Derived2 *pC2 = pB2->Clone();//在編譯階段就可檢知Derived2沒有實作Clone
>>}
>
>由於你用法不當,導致觀點偏差,所以就一直在各說各法
>你應該由 Base 的使用角度去考量,而不是由 Derived
這只是提供一個可以在編譯階段可以檢知有沒有實作Clone的方法。
Clone使用covariant return types是標準的用法,
PC2你把它當作Base使用也完全沒問題。關什麼用法不當?
有關must和轉型的討論夠多了,我提出了很多各個角度的觀點(還有正式文件),請問你提出了什麼?
就直接下結論是別人的觀點W差?
反正must真正的需求我是感受不到,clone也不是好東西(我們都是以layer管理階層物件建立和行為許可),那個assert也是削足適履。
,既無共識,討論就到此吧。

Base *Fun(Base *pBase)
{
  return pBase->Clone();
}
作者 : cxxlman(CxxlMan) C++優秀好手貼文超過1000則
[ 貼文 1003 | 人氣 3227 | 評價 1260 | 評價/貼文 1.26 | 送出評價 27 次 ] 
[ 給個讚 ]  [ 給個讚 ]  [ 回應本文 ]  [ 發表新文 ]  [ 回上頁 ] [ 回討論區列表 ] [ 回知識入口 ]
2017/12/20 下午 04:44:41

>>int main()
>>>{
>>> Derived1 *pB1 = new Derived1;
>>> Derived2 *pB2 = new Derived2;
>>>
>>> Derived1 *pC1 = pB1->Clone();
>>> Derived2 *pC2 = pB2->Clone();//在編譯階段就可檢知Derived2沒有實作Clone
>>>}
>>
>>由於你用法不當,導致觀點偏差,所以就一直在各說各法
>>你應該由 Base 的使用角度去考量,而不是由 Derived
>這只是提供一個可以在編譯階段可以檢知有沒有實作Clone的方法。
>Clone使用covariant return types是標準的用法,
>PC2你把它當作Base使用也完全沒問題。關什麼用法不當?
>有關must和轉型的討論夠多了,我提出了很多各個角度的觀點(還有正式文件),請問你提出了什麼?
>就直接下結論是別人的觀點W差?
>反正must真正的需求我是感受不到,clone也不是好東西(我們都是以layer管理階層物件建立和行為許可),那個assert也是削足適履。
>,既無共識,討論就到此吧。
>
>Base *Fun(Base *pBase)
>{
> return pBase->Clone();
>}

Clone() 是 Prototype pattern 的主要功能,本來就是由 Base 的角度來處理,你可以先去了解一下 Prototype pattern
作者 : sunyear(coco) VC++卓越專家C++頂尖高手貼文超過2000則
[ 貼文 2421 | 人氣 1485 | 評價 6060 | 評價/貼文 2.5 | 送出評價 5 次 ] 
[ 給個讚 ]  [ 給個讚 ]  [ 回應本文 ]  [ 發表新文 ]  [ 回上頁 ] [ 回討論區列表 ] [ 回知識入口 ]
2017/12/20 下午 10:08:16
>Clone() 是 Prototype pattern 的主要功能,本來就是由 Base 的角度來處理,你可以先去了解一下 Prototype pattern
從OO(P)到轉型,都隨便丟個名詞,讓我拚命解說。要別人幫你讀書,也不用老是來這一招,好嗎?
covariant return type 常在Prototype pattern一起談的,clone return variant type 就不是Prototype pattern了嗎?
那也是標準用寫法(之一)
return base(interface),操作基本上全由base(interface)進行,這是比較理想的Prototype pattern,我知道啊!
前面不是說了嗎使用covariant type目的只在於應你不情之請,多個檢查Derived是否有實作Clone而己。
程式要全部使用base(interface)去操作,沒人給你限制。這違反了什麼 Base 的角度?
不要你自己把Base轉型回來Derived做例子,很OK,別人Clone回來Derived就抓狂。
兩者目的都是一樣的在檢查衍生類別是否有實作clone,只是一個是動態檢查,一個是靜態檢查。
至於這種檢查,你覺的很重要,我覺得沒意義而己。
作者 : cxxlman(CxxlMan) C++優秀好手貼文超過1000則
[ 貼文 1003 | 人氣 3227 | 評價 1260 | 評價/貼文 1.26 | 送出評價 27 次 ] 
[ 給個讚 ]  [ 給個讚 ]  [ 回應本文 ]  [ 發表新文 ]  [ 回上頁 ] [ 回討論區列表 ] [ 回知識入口 ]
2017/12/21 上午 10:16:13
還是去看看吧,否則缺乏溝通基礎,一直雞同鴨講各說各話也不是辦法
作者 : cxxlman(CxxlMan) C++優秀好手貼文超過1000則
[ 貼文 1003 | 人氣 3227 | 評價 1260 | 評價/貼文 1.26 | 送出評價 27 次 ] 
[ 給個讚 ]  [ 給個讚 ]  [ 回應本文 ]  [ 發表新文 ]  [ 回上頁 ] [ 回討論區列表 ] [ 回知識入口 ]
2017/12/22 下午 07:13:29
用一個範例做個總結,也感謝 coco 提供的 assert,在 C++ 沒有提供 must 之下,還能做到類似的效果,
讓程式簡潔不少,也由此看出 must 的須要性

範例主要展示 prototype pattern 及 visitor pattern 的架構,不過 visitor pattern 做了改良,可以
採用 ClassID 做配對,可以在執行時期做較機動的處理,但為了減少篇幅和難度,還是寫死未做較機動的
處理

class C 是為了表現未強制實作的後果,有幾處做了 remark,把 remark 的地方恢復,就可正常執行

#include <map>
#include <memory>
#include <string>
#include <assert.h>   /* assert */
#include <typeinfo>   /* assert */
#include <iostream>

using namespace std;

// 插件的識別碼
#define Base_ClassID u8"14432A2FAECE4DA7A33CDA7472FB61DE"
#define A_ClassID u8"9EED77B5BD97404B85A390E2806FE435"
#define B_ClassID u8"11B5DCB493744F9B9A7C14343CF609A6"
#define C_ClassID u8"20DB108E78284B8390D258A4626523AC"

class Visitor;
class Base;

class Prototype
{
 virtual Prototype *Clone() const = 0;
public:
};

class Acceptor
{
 virtual string GetClassID() const = 0;
public:

 virtual void Accept(Visitor *v) = 0;

 friend class Visitor;
};

class Visitor
{
protected:
 string GetClassID(Acceptor *pAcceptor) const
 {
  return pAcceptor->GetClassID();
 }

 virtual void Visit(Acceptor *pAcceptor) = 0;
public:
 
 friend class Base;
};

class Base:public Prototype, public Acceptor
{
 virtual string GetClassID() const override
 {
  // 不准許 Base 被繼承,卻沒有覆寫 GetClassID()
  assert(strcmp(typeid(Base).name(), typeid(*this).name()) == 0);
  return Base_ClassID;
 }
 virtual void Accept(Visitor *v) override final
 {
  v->Visit(this);
 }

public:
 virtual Base *Clone() const override
 {
  // 不准許 Base 沒有被繼承,還執行 Clone()
  assert(strcmp(typeid(Base).name(), typeid(*this).name()) != 0);
  return new Base(*this);
 }
};
作者 : cxxlman(CxxlMan) C++優秀好手貼文超過1000則
[ 貼文 1003 | 人氣 3227 | 評價 1260 | 評價/貼文 1.26 | 送出評價 27 次 ] 
[ 給個讚 ]  [ 給個讚 ]  [ 回應本文 ]  [ 發表新文 ]  [ 回上頁 ] [ 回討論區列表 ] [ 回知識入口 ]
2017/12/22 下午 07:14:59
/***/
class CustomVisitor
{
public:
 virtual void Visit(Acceptor *pAcceptor) const = 0;
};

class VisitorManager :public Visitor
{
 map<string, shared_ptr<CustomVisitor> > m_CustomVisitorMap;

 virtual void Visit(Acceptor *pAcceptor) override
 {
  string ID = GetClassID(pAcceptor);
  shared_ptr<CustomVisitor> CustomVisitor_Ptr = m_CustomVisitorMap[ID];

  if (CustomVisitor_Ptr)
   CustomVisitor_Ptr->Visit(pAcceptor);
  else
   cout << "物件找不到相應的 CustomVisitor" << endl;
 }
public:
 VisitorManager();
};

class ObjPool
{
 map<string, shared_ptr<Base> > m_BaseMap;
public:
 ObjPool();
 Base *Get(string ClassID)
 {
  shared_ptr<Base> Src = m_BaseMap[ClassID];
  if (Src)
   return Src->Clone();
  else
   return nullptr;
 }
};

/***/
class BaseVisitor :public CustomVisitor
{
 virtual void Visit(Acceptor *pAcceptor) const override
 {
  Base *pBase = static_cast<Base*>(pAcceptor);
  cout << "Base 被 Clone,請檢查..." << endl;
 }
public:
};

class A :public Base
{
 string m_Name{ "class A" };

 virtual string GetClassID() const override
 {
  // 不准許 A 被繼承,卻沒有覆寫 GetClassID()
  assert(strcmp(typeid(A).name(), typeid(*this).name()) == 0);
  return A_ClassID;
 }
 virtual A *Clone() const override
 {
  // 不准許 A 被繼承,卻沒有覆寫 Clone()
  assert(strcmp(typeid(A).name(), typeid(*this).name()) == 0);
  return new A(*this);
 }

public:
 void SayHI()
 {
  cout << "HI! My name is " << m_Name << endl;
 }
};

class B :public Base
{
 string m_Name{ "class B" };

 virtual string GetClassID() const override
 {
  // 不准許 B 被繼承,卻沒有覆寫 GetClassID()
  assert(strcmp(typeid(B).name(), typeid(*this).name()) == 0);
  return B_ClassID;
 }
 virtual B *Clone() const override
 {
  // 不准許 B 被繼承,卻沒有覆寫 Clone()
  // assert(strcmp(typeid(B).name(), typeid(*this).name()) == 0); // remark
  return new B(*this);
 }

public:
 void SayHello()
 {
  cout << "Hello! My name is " << m_Name << endl;
 }
};
作者 : cxxlman(CxxlMan) C++優秀好手貼文超過1000則
[ 貼文 1003 | 人氣 3227 | 評價 1260 | 評價/貼文 1.26 | 送出評價 27 次 ] 
[ 給個讚 ]  [ 給個讚 ]  [ 回應本文 ]  [ 發表新文 ]  [ 回上頁 ] [ 回討論區列表 ] [ 回知識入口 ]
2017/12/22 下午 07:16:48
class C :public B
{
 int Age{ 100 };

 virtual string GetClassID() const override
 {
  // 不准許 C 被繼承,卻沒有覆寫 GetClassID()
  assert(strcmp(typeid(C).name(), typeid(*this).name()) == 0);
  return C_ClassID;
 }

  // remark
 /*
 virtual C *Clone() const override
 {
  // 不准許 B 被繼承,卻沒有覆寫 Clone()
  assert(strcmp(typeid(C).name(), typeid(*this).name()) == 0);
  return new C(*this);
 }
 */
public:
 void HowOld()
 {
  cout << "I am " << 50 << " years old" << endl;
 }
};

class AVisitor :public CustomVisitor
{
 virtual void Visit(Acceptor *pAcceptor) const override
 {  
  // assert(strcmp(typeid(A).name(), typeid(*pAcceptor).name()) == 0);

  A *pA = static_cast<A*>(pAcceptor);
  pA->SayHI();
  cout << "A 複製成功..." << endl;
 }
public:
};

class BVisitor :public CustomVisitor
{
 virtual void Visit(Acceptor *pAcceptor) const override
 {
  // assert(strcmp(typeid(B).name(), typeid(*pAcceptor).name()) == 0);

  B *pB = static_cast<B*>(pAcceptor);
  pB->SayHello();
  cout << "B 複製成功..." << endl;
 }
public:
};

class CVisitor :public CustomVisitor
{
 virtual void Visit(Acceptor *pAcceptor) const override
 {
  // assert(strcmp(typeid(C).name(), typeid(*pAcceptor).name()) == 0);

  C *pC = static_cast<C*>(pAcceptor);
  pC->SayHello();
  pC->HowOld();
  cout << "C 複製成功..." << endl;
 }
public:
};
作者 : cxxlman(CxxlMan) C++優秀好手貼文超過1000則
[ 貼文 1003 | 人氣 3227 | 評價 1260 | 評價/貼文 1.26 | 送出評價 27 次 ] 
[ 給個讚 ]  [ 給個讚 ]  [ 回應本文 ]  [ 發表新文 ]  [ 回上頁 ] [ 回討論區列表 ] [ 回知識入口 ]
2017/12/22 下午 07:17:14

ObjPool::ObjPool()
{
 // 註冊所有 Base 的 Derived
 m_BaseMap[string(Base_ClassID)] = shared_ptr<Base >(new Base);
 m_BaseMap[string(A_ClassID)] = shared_ptr<Base >(new A);
 m_BaseMap[string(B_ClassID)] = shared_ptr<Base >(new B);
 m_BaseMap[string(C_ClassID)] = shared_ptr<Base >(new C);
}

VisitorManager::VisitorManager()
{
 // 註冊所有 CustomVisitor 的 Derived
 m_CustomVisitorMap[string(Base_ClassID)] = shared_ptr<CustomVisitor >(new BaseVisitor); 
 m_CustomVisitorMap[string(A_ClassID)] = shared_ptr<CustomVisitor >(new AVisitor);
 m_CustomVisitorMap[string(B_ClassID)] = shared_ptr<CustomVisitor >(new BVisitor);
 m_CustomVisitorMap[string(C_ClassID)] = shared_ptr<CustomVisitor >(new CVisitor);
}

int main()
{
 ObjPool MyObjPool;
 VisitorManager MyVisitorManager; 

 shared_ptr<Base> Obj_Ptr(MyObjPool.Get(string(A_ClassID)));
 shared_ptr<Acceptor> Acceptor_Ptr = static_pointer_cast<Acceptor>(Obj_Ptr);
 Acceptor_Ptr->Accept(&MyVisitorManager);

 cout << endl;
 Obj_Ptr = shared_ptr<Base>( MyObjPool.Get(string( C_ClassID )) );
 Acceptor_Ptr = static_pointer_cast<Acceptor>(Obj_Ptr);
 Acceptor_Ptr->Accept(&MyVisitorManager); 

 return 0;
}

 板主 : simula
 > C++ - 討論區
 - 最近熱門問答精華集
 - 全部歷史問答精華集
 - C++ - 知識庫
  ■ 全站最新Post列表
  ■ 我的文章收藏
  ■ 我最愛的作者
  ■ 全站文章收藏排行榜
  ■ 全站最愛作者排行榜
  ■  月熱門主題
  ■  季熱門主題
  ■  熱門主題Top 20
  ■  本區Post排行榜
  ■  本區評價排行榜
  ■  全站專家名人榜
  ■  全站Post排行榜
  ■  全站評價排行榜
  ■  全站人氣排行榜
 請輸入關鍵字 
  開始搜尋
 
Top 10
評價排行
C++
1 Raymond 13050 
2 青衫 4760 
3 simula 4690 
4 coco 4030 
5 白老鼠(Gary) 3670 
6 ozzy 2540 
7 Ben 2250 
8 Anderson 1960 
9 windblown 1650 
10 Kenny 1560 
C++
  專家等級 評價  
  一代宗師 10000  
  曠世奇才 5000  
  頂尖高手 3000  
  卓越專家 1500  
  優秀好手 750  
Microsoft Internet Explorer 6.0. Screen 1024x768 pixel. High Color (16 bit).
2000-2018 程式設計俱樂部 http://www.programmer-club.com.tw/
0.8125