如何将一个基类对象赋值给一个派生类对象

4

我有一个关于C++的问题,如何将一个基类对象赋值给派生类对象?或者如何将指向基类对象的指针赋值给指向派生类对象的指针?

在下面的代码中,这两行是错误的。如何纠正?

#include <iostream>
using namespace std;
class A{
public:
   int a;
};

class B:public A{
public:
   int b;
};

int main(){
    A a;
    B b;
    b = a; //what happend?
    cout << b.b << endl;   

    B* b2;
    b2 = &a;        // what happened?
    cout << b->b << endl;
}
5个回答

4

将基对象分配给派生对象(或将基指针分配给派生指针)是没有意义的,因此C++会尽力阻止您这样做。但有一个例外情况,当基指针确实指向派生对象时,您可以使用dynamic_cast:

base * p = new derived;
derived * d = dynamic_cast <derived *>( p );

在这种情况下,如果p实际上指向一个基类,指针d将包含NULL。

然而,如果类不包含任何虚函数,则似乎不允许使用dynamic_cast。您可以使用static_cast,但这意味着您要承担全部责任。 - UncleBens
@UncleBens 如果基类不包含任何虚函数,我必须问一下为什么您要从它派生 - 如果您打算用它进行派生,则应该至少包含一个虚析构函数。 - anon
实际上,仅继承数据的继承是可疑的。在这种情况下,我建议不要继承,而是给B一个A成员,并提供一个接受A的构造函数。然后您可以使用构造函数进行“转换”。 - UncleBens

3

当一个对象在堆栈上时,你只能将相同类型的对象彼此赋值。它们可以通过重载的强制转换运算符或重载的赋值运算符进行转换,但这时你正在指定一种转换。编译器本身无法进行这样的转换。

A a;
B b;
b = a;

在这种情况下,您试图将 A 分配给 B,但 A 不是 B,因此无法工作。
A a;
B b;
a = b;

这种方法确实可以起作用,但它可能不是您所期望的。你只是切割了你的B对象。B是A的一种,所以赋值是可以进行的,但因为它在堆栈中,它只会将B中属于A的部分赋值给a对象。所以,你得到的是一个A对象。尽管你从B对象中赋值过来,它并不是一个B对象。

如果你真的想要把一种类型的对象赋值给另一种类型的对象,它们需要是指针。

A* pa = NULL;
B* pb = new B;
pa = pb;

这个代码可以正常运行。现在pa指向pb,因此它仍然是一个B。如果类A和B都有虚函数,并且B覆盖了A的虚函数,那么当你在pa上调用这些函数时,它们将调用B版本(非虚函数仍然调用A版本)。

A* pa = new A;
B* pb = pa;

这段代码无法正常工作。pa指向的不是B,因此你不能将其赋值给pb,pb必须指向B。仅仅因为B是A的一种形式,并不意味着A就是B。

A a;
B* pb = &a;

这个代码和之前的那个一样都是有问题的。只不过这次 A 是在栈上而不是堆上。

A* pa;
B b;
pa = &b;

这是可行的。b是B,B是A,所以A可以指向它。虚函数会调用B版本,非虚函数会调用A版本。

因此,基本上,A*可以指向B,因为B是A。但是B*不能指向A,因为它不是B。


2

编译器不允许这种情况。即使你通过某种强制类型转换的技巧实现了,这样做也没有意义。将派生对象分配给基础指针是有意义的,因为基础部分能够做到的一切,派生部分也能做到。但是,如果允许相反的情况,如果您尝试在基本对象上访问定义在派生中的成员,会发生什么呢?您将尝试访问一个填充有垃圾或无关数据的内存区域。


1
b = a; //what happend?

这是绝对不合法的 - A 不等于 B,所以你不能这样做。

b2 = &a;        // what happened?

我也是。

无论哪种情况,编译器都不知道要给 int b 分配什么值,因此会阻止您这样做。反过来(将 Derived 赋值给 Base),可以正常工作,因为 Base 是 Derived 的一个子集

现在,如果您告诉我们您想实现什么,我们可能可以帮助您。

如果需要为已知为 Derived 类型的 A 进行赋值,则可以使用转换:

A* a = new B();
B* b = dynamic_cast<B>(a);

请记住,如果 a 不是 B,那么 dynamic_cast 将返回 NULL。请注意,这种方法仅适用于指针,有其原因。


还适用于引用,在失败的情况下会抛出异常。然而,dynamic_cast似乎需要虚拟方法(例如在A中添加虚拟析构函数)。 - UncleBens

0

派生对象是基础对象的一种,而不是相反。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接