2011-03-05 50 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任何值因此错误。

如果你真的想要这个功能,你将不得不重载赋值操作符来处理这种情况。

相关问题