簡體   English   中英

c ++多態((X *)y) - > foo()vs((X)* y).foo()

[英]c++ polymorphism ((X*)y)->foo() vs ((X)*y).foo()

假設Y是來自類X的派生類,X將foo聲明為虛擬。 假設y是(Y *)類型。 然后((X *)y) - > foo()將執行Y版本的foo(),但是((X)* y).foo()將執行X版本。 你能告訴我為什么多態性不適用於解引用的案例嗎? 我希望任何一種語法都會產生Y版本的foo()。

您正在切割Y對象部件並將對象復制到X對象中。 然后調用的函數在X對象上調用,因此調用X的函數。

當您在聲明或強制轉換中指定C ++中的類型時,這意味着聲明或轉換為對象實際上是該類型的對象,而不是派生類型。

如果你只想處理對象是X類型(也就是說,如果你想要表達式的靜態類型是X ,但仍希望它表示Y對象)那么你轉換為引用類型

((X&)*y).foo()

這將調用Y對象中的函數,並且不會切片也不會復制到X對象中。 在步驟中,這樣做

  • 取消引用指針y ,其類型為Y* 解除引用會產生類型為Y左值表達式。 左值表達式實際上可以表示派生類型的對象,即使其靜態類型是其基類之一。
  • 轉換為X& ,這是對X的引用。 這將產生類型X左值表達式。
  • 調用該函數。

你原來的演員做了

  • 取消引用指針y
  • 結果表達式轉換為X 這將導致復制操作到新的X對象。 結果表達式是靜態類型X右值表達式。 表示的對象的動態類型也是 X ,與所有rvalue表達式一樣。
  • 調用該函數。

轉換always(*)創建一個您要轉換為的類型的新對象,該對象是使用您正在轉換的對象構造的。

轉換為X *會創建一個新指針(即X *類型的對象)。 它具有與y相同的值,因此它仍指向Y類型的同一對象。

轉換為X會創建一個新的X.它是使用*y構造的,但它與舊對象無關。 在您的示例中, foo()在此新“臨時”對象上調用,而不是在y指向的對象上調用。

你是正確的,動態多態只適用於指針和引用,而不適用於對象,這就是為什么:如果你有一個指向X的指針,那么它指向的東西可能是X的子類。但是如果你有一個X,然后它是一個X,沒有別的。 虛擬通話毫無意義。

(*)除非優化允許省略不改變結果的代碼。 但是不允許優化改變調用foo()函數。

解除引用( *y部分)很好,但是強制轉換( (X)部分)創建了一個特定於X類的新(臨時)對象 - 這就是強制轉換的含義 因此,對象必須具有來自類X的虛擬表 - 考慮到轉換將刪除子類中由Y添加的任何實例成員(實際上, X如何復制ctor可能知道它們?),所以它如果任何Y的覆蓋都要執行,那么這可能是一場災難 - 他們知道this是一個Y的實例,加上成員和所有......當知識是假的時候!

你投射指針的版本當然是完全不同的 - *XY*具有相同的位,所以它仍然指向一個完全有效的Y實例(當然,它指向y ,當然)。

可悲的事實是,為了安全起見,類的副本應該只作為參數調用該類的實例 - 而不是任何子類; 增加實例成員和c的損失太具破壞性了。 但確保這一點的唯一方法是遵循Haahr的優秀建議,“不要強化具體類”......盡管他正在撰寫有關Java的文章,但建議至少與C ++一樣好(有這個副本ctor“切片“問題另外! - )

我認為這僅僅是由於語言的指定方式。 引用和指針盡可能使用后期綁定,而對象使用早期綁定。 在每種情況下(我想象)都可以進行后期綁定,但是那樣做的編譯器不會遵循C ++規范。

我認為Darth Eru的解釋是正確的,這就是為什么我認為C ++的行為方式如下:

代碼(X)* y就像創建一個X類型的局部變量一樣。編譯器需要在堆棧上分配sizeof(X)空間,並拋棄Y類型對象中包含的任何額外數據,所以當你調用foo()它必須執行X版本。 編譯器很難以一種讓你調用Y版本的方式運行。

代碼(X *)y就像創建指向對象的指針一樣,編譯器知道指向的對象是X或X的子類。在運行時,當你取消引用指針並用“ - > foo()調用foo時“確定對象的類,並使用正確的函數。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM