为什么我们在进行向下转换时需要进行类型转换?
假设我有一个基类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;
}
答案 0 :(得分: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!
查看错误和行号:
但是,如果以下列方式重载operator=
,
struct Apple : Fruit
{
Apple & operator = (const Fruit &);
};
然后不需要显式转换。你可以写
apple = fruit; //alright - assignment (not construction)
现在,如果你添加这个:
struct Apple : Fruit
{
Apple(const Fruit &);
};
然后你可以写
Apple apple = fruit ; //ok - construction ( not assignment)
答案 1 :(得分:2)
从项目的角度考虑它。说class B
是一个球,class D
是一个躲避球。然后对于所有躲避球,它们都是球(因此对于所有class D
它们都是class B
)。但是,并非所有球都是躲避球,因此class B
不一定是class D
。这只是一个快速解释,并没有给出任何真正的技术原因 - 但我希望这有助于理解!
的问候,
丹尼斯M。
答案 2 :(得分: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个字节。
答案 3 :(得分:0)
考虑这个逻辑:
D是B。
但B不一定是D。
因此,如果您可以将B类型传递给D,则无法保证。
D可能还有其他未在Base中实现的东西(D工作所需的更多方法,B就是没有)。
因此,当你使用Downcast时,你会让C ++将B的内存区域视为D ...这可能会导致运行时错误。
答案 4 :(得分:0)
由于D来自B,因此D具有B的所有功能以及它自己的一些功能。现在,当您将类型B的对象分配给D时,生成的对象无法完全构造,因为无法知道如何填充D的功能值。
考虑以下示例扩展 -
B级
{
int memberOfB;
};
D类:公共B
{
int memberOfD;
};
现在D有两个成员,memberOfB(通过继承)和memberOfD(自己的成员) 当您尝试d = b时,可以分配memberOfB的值,但编译器无法将任何值分配给memberOfD,从而导致错误。
如果您确实需要此功能,则必须重载赋值运算符以处理此方案。