“向上转型”的缺点是什么?

3
抽象类的目的不是让开发人员创建基类的对象并进行向上转换,据我所知。现在,即使不需要向上转换,我仍然使用它,这是否证明在某种程度上“不利”?更多说明:通常在设计中,您希望基类仅为其派生类提供接口。也就是说,您不希望任何人实际上创建基类的对象,只能将其向上转换以使用其接口。这可通过使该类抽象来实现。通过向上转换,我指的是:baseClass *obj = new derived();

不,它并不会证明“不利”,它使用了替换原则。 - jmj
2
static_cast<Base*>( pDerived ) 是一种*向上转型(upcast)*。这是你想要的吗? - Cheers and hth. - Alf
1
你是否混淆了向上转型和向下转型?向上转型是完全可以的,也是多态性能够发挥作用的必要条件。 - Björn Pollex
1
首先,“向上转型”是从派生类到基类,所以我不理解“创建一个基类对象”的部分。 - sharptooth
@AlfP.Steinbach,我所说的向上转型是指baseClass *obj = new derived(); - Aquarius_Girl
4个回答

4

向上转型对于非多态类可能不利。例如:

class Fruit { ... };  // doesn't contain any virtual method
class Apple : public Fruit { ... };
class Blackberry : public Fruit { ... };

将其向上转换并放置在某个地方。

Fruit *p = new Apple;  // oops, information gone

现在,如果没有任何手动机制,您将无法知道*pApple还是Blackberry的实例。

[请注意,对于非多态类,不允许使用dynamic_cast<>]。


谢谢,但是我只是在谈论多态类 :) - Aquarius_Girl
@AnishaKaul,好的,我回答这个问题是因为你的问题中没有提到那一点。顺便说一下,对于多态类来说,向上转型是一项基本功能,它不会有危险性。 - iammilind

2

抽象类用于表达一组(子)类共有的概念,但是不合理创建实例。

考虑一个类Animal。创建该类的实例是没有意义的,因为没有仅仅是动物的东西。有鸭子、狗和大象,它们都是Animal的子类。通过正式声明Animal类,您可以捕获所有类型动物的相似之处,并通过将其设为抽象来表达它无法被实例化的特性。

在静态类型语言中,需要进行向上转型以利用多态性。正如@Jigar Joshi在评论中指出的那样,这被称为Liskov替换原则

编辑:向上转型并不是不利的。事实上,您应该尽可能使用它,使您的代码依赖于超类(接口),而不是基类(实现)。这使得您以后可以在不必更改代码的情况下切换实现。


我知道抽象类的含义。我的问题是,如果没有必要,使用“向上转型”是否有什么不利之处? - Aquarius_Girl
@AnishaKaul:我添加了一个关于那个的段落。 - Björn Pollex
抱歉,我没有正确理解“使您的代码依赖于超类(接口)而不是基类(实现)”的含义。我们不是在谈论基类和派生类吗?什么是超类,它的功能是什么? - Aquarius_Girl
2
超类是基类的另一个说法,抱歉混淆了。 - Björn Pollex
1
向上转型并不是一个不利的事情。事实上,只要你处理多态类,就应该尽可能地使用它。 - iammilind

1

向上转型是一种技术工具。

像每个工具一样,如果使用不当,它会有危险或者不利之处。只有在特定的编程范式下,才能使代码更加“纯粹”,从而使其具有优势或劣势。

C++并不一定是“纯面向对象”的,也不一定是“纯泛型”的,也不一定是“纯函数式”的。由于C++是一种“实用主义语言”,因此通常不会强制将其适应于“唯一的范式”。

从技术角度来说,唯一可以说的是:

  • 派生类是基类加上更多内容
  • 通过基类指针引用派生类会使得“更多内容”无法访问,除非在基类中提供了机制来跳转到派生类作用域。
  • C++提供的隐式跳转机制是虚函数。
  • C++提供的显式跳转机制是dynamic_cast(用于向下转型)。
  • 对于非多态对象(没有任何虚方法),仍然可以使用static_cast(向下转型),但没有运行时检查。

从所有这些点的一致和不一致使用中得出优缺点。这不仅与向下转换有关。


0

一个缺点是派生类中引入的新功能明显丢失了:

class A
{
   void foo();
}

class B : public A
{
   void foo2();
}

A* b = new B;
b->foo2(); //error - no longer visible

我在这里谈论的是非虚函数。

此外,如果您忘记将析构函数设置为虚函数,则可能会在通过指向基对象的指针删除派生对象时出现一些内存泄漏。

但是,所有这些都可以通过良好的架构来避免。


1
我认为这个例子没有解释向上转型的缺点。foo2()B类的成员而不是A类的成员,因此它从未被看到过。一旦你写了一个非虚拟的A::foo2(),它就会悄悄地出现!(注意:我没有对这个答案进行投票) - iammilind
@iammilind,完全正确。但是b是B类型的一个对象...我已经提到过,有了良好的设计,这并不是真正的障碍。 - Luchian Grigore
1
@LuchianGrigore - 你的观点是正确的,但你的例子不是。在你的例子中,你只会得到一个普通的编译错误。 - littleadv

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