2011-03-05 72 views
2

爲什麼我們需要在下注時進行類型轉換?爲什麼向下轉換需要類型轉換才能工作?

假設我有一個基類B和一個派生類D.現在我分別創建兩個B,D的實例b,d。爲什麼d = b是不可能的,即使d是b的所有東西。然而相反的(即b = d)是可能的。

class B 
{ 
    /* body */ 
}; 
class D : public B 
{ 
    /* body */ 
}; 

void f() 
{ 
    B b; 
    D d; 

    b = d;  /* OK */ 
    d = b;  /* Not OK */ 

return; 
} 
+2

請注意,在第一個賦值「b = d」中,該對象將被*切片*以將其轉換爲「B」實例。這可能不是你通常想要或期望的。 – Thomas 2011-03-05 18:26:23

回答

2

想象它的項目。說class B是一個球,class D是一個躲避球。然後對於所有的躲避球,它們是球(因此對於所有class D它們是class B)。但是,並非所有的球都是躲避球,所以class B不一定是class D。這只是一個快速解釋,並沒有給出任何真正的技術原因 - 但我希望這有助於理解!

問候,
丹尼斯M.

+0

這是高層次的解釋。但是限制它的內部機制是什麼? – 2011-03-05 18:27:29

+0

@ paper.plane:查看Nawaz的回覆以及Thomas對原帖的評論 – RageD 2011-03-05 18:29:24

3

這是合乎邏輯的。每個派生的對象都是基礎對象,因此上傳是自動的。但是每個基礎對象都不是派生的,因此你需要明確地施放它。

看到這個簡單的例子:

struct Fruit {};  
struct Apple : Fruit {}; 

Apple apple; 
Fruit fruit; 
fruit = apple; //ok - automatic - since apple is fruit (see "object slicing") 
apple = fruit; //error - not automatic - since fruit is necessarily apple! 

請參閱錯誤和行號:

http://ideone.com/JnLc2


但是,如果你重載以下列方式operator=

struct Apple : Fruit 
{ 
    Apple & operator = (const Fruit &); 
}; 

然後顯式轉換不需要。你可以寫

apple = fruit; //alright - assignment (not construction) 

看到這個:http://ideone.com/IGbFI

現在,如果你補充一點:

struct Apple : Fruit 
{ 
    Apple(const Fruit &); 
}; 

,那麼你可以寫

Apple apple = fruit ; //ok - construction (not assignment) 

看到這個:http://ideone.com/muQc0

+0

但是,Derived對象能夠容納Base對象擁有的所有內容,而不像上一次投射那樣,Base對象沒有能力容納Derived對象的額外參數導致OBJECT SLICING。 – 2011-03-05 18:29:57

+1

@ paper.place:是的,但派生類的其餘部分仍未定義,因爲當您編寫「derived = base」時,只能更改派生的基本子對象,這可能會引入不一致,那就是不符合Object-ness的邏輯! – Nawaz 2011-03-05 18:34:29

0

'b'是'd'功能的子集。當將'b'轉換爲'd'時,只能訪問'B'成員和方法,所以這是安全的。然而,將'd'投射到'b'可能會導致問題,因爲'D'是超級'B'。

這裏的一個更相關的例子:

struct B 
{ 
    int m_variable; 
}; 

struct D: public B 
{ 
    int m_other_variable; 
}; 


void function() 
{ 
    B b; 
    D *d; 

    d = (D*)&b; //not a safe cast! 

    d->m_other_variable = 101; // this isn't inside 'B' but beyond it. What this will //probably do is set the pointer 'd' to 101 but it might not: defined behaviour land! 


} 

「d」的實例包含在尺寸上既「m_variable」和「m_other_variable」和至少爲8個字節。另一方面,'B'的實例只包含'm_variable',並且只有至少4個字節的大小。

+0

語義:'D'是'B'的一個* subset *,因爲每個'D'都是'B',但反過來也不是。 'D'的*功能*,但是,是'B'的功能的超集。 – Thomas 2011-03-05 18:27:41

+0

哦,現在你正在爭論語義:)雖然採取了點。 – Luther 2011-03-05 18:33:12

0

考慮這樣的邏輯:

d是B.

但B是不一定是D.

所以沒有保證,如果你能通過B型爲D.

d可能有未在基本實現其他的東西( D工作所需的更多方法,B只是沒有)。

所以當你Downcast時,你會讓C++把B的內存區域當作D ......這可能會導致運行時錯誤。

0

由於D來自B,所以D具有B的所有功能加上它自己的一些功能。現在,當將B類型的對象分配給D時,由於無法知道如何填充D的功能值,因此無法完全構造生成的對象。

考慮您的示例的以下擴展 -

B類
{
INT memberOfB;
};
D類:公共B
{
int memberOfD; };

現在d有兩個成員,memberOfB(直通繼承)和memberOfD(自己的成員) 當您嘗試d = B,memberOfB的值可以分配,但沒有辦法讓編譯器分配給memberOfD任何值因此錯誤。

如果你真的想要這個功能,你將不得不重載賦值操作符來處理這種情況。

相關問題