【问题标题】:C++ polymorphic cloning: How to obtain a Derived pointer from a Base pointer?C++ 多态克隆:如何从基指针获取派生指针?
【发布时间】:2015-09-09 09:44:43
【问题描述】:

这已经是我发送的几篇关于这个 subjet 的帖子,如果这让你们中的一些人感到困扰,我深表歉意。在玩了几天并尝试了不同的替代方案(模式)后,我得出了以下结论,这使我能够更好地提出我的问题。

我有一个Base * 的向量,并且有许多从 Base 派生的 Derived<T> 类。不同的类参数 T 以前向声明的开头声明。我需要的是从这个vector<Base *> 中获取一对对象并使用模板应用function(Derived<T1> obj1, Derived<T2> obj2) 以进行某种双重调度。但是,当我从向量中获取一个元素时,我无法将指向 Base 的指针转换为指向 Derived 的指针。因此,我不能使用function(Derived<T1> obj1, Derived<T2> obj2)

当然,这可以简单地使用dynamic_cast<Derived<T> *>(base_ptr) 来实现。但是,这不是我想要的,因为要这样做,必须事先知道 T,这里不是这样。我必须能够从向量中选择任何元素,而无需知道它们的 Derived<T> 类型。

因此,我尝试了多态克隆。尽管它使用协变返回类型,但不幸的是,它不起作用。正如不同人在SO中解释的那样,根据C++标准,原因是在编译时,编译器仍然不知道对象的完整类型。因此,尽管人们期望 clone() 应该返回一个 Derived<T> *,但它返回一个 Base *

接下来的几行代码表达了我的意思。 一般来说,多态克隆用于以下情况:

Base *b1 = new Derived;
Base *b2;
b2 = b1->clone();

这不是我想要的。我需要的是一个指向派生的指针:

Base *b1 = new Derived;
Derived *d1;
d1 = b1->clone(); // This is what I want, but it fails.

在这里,编译器抱怨说不能将Base * 类型的对象分配给Derived * 类型的对象。因此,我不能在function(,) 中使用*d1

我也尝试了非虚拟接口(NVI)的方法,但是没用。

有没有人知道如何解决上面的简单代码,特别是最后一行?非常感谢。

【问题讨论】:

标签: c++ polymorphism cloning covariant-return-types


【解决方案1】:

你想要什么是不可能的,因为不能保证Base *指向的对象实际上是Derived的一个实例。

您能做的最多就是为您的clone() 函数使用协变返回类型。

class Base
{
public:
    virtual Base* clone() const
    {
        return new Base(*this);
    };
    virtual ~Base() {};
};

class Derived : public Base
{
public:
    virtual Derived* clone() const    // note different return type
                                      // this does override the inherited version
    {
        return new Derived(*this);
    }
};


int main()
{
    Base *b = new Derived;
    Derived *d1 = b->clone();    // this does not work

    Derived *o = new Derived;
    Derived *d = o->clone();    // this works
}

如果您想从Base * 克隆到Derived *,则需要进行运行时检查并强制进行类型转换。

Base *b = new Derived;

Base *temp = b->clone();
Derived *d = dynamic_cast<Derived *>(temp);
if (d)
{
     // d is actually an instance of Derived so can be used as such
}
else
{
     //  The cloning did not produce a Derived.  Cope with it
}
delete temp;   // this will delete the object pointed to by d, since it is the same object

不过,一般来说,您要问的是设计有缺陷的积极指标。一般而言(假设您需要一个具有多态基础的类层次结构),您需要设计Base,因此它的接口(特别是一组可能由派生类专门化的虚函数)足以让该类的所有用户做他们需要的。 Base 的所有用户都应该需要一个指向Base 的指针或引用(即Base &amp;Base *),并且他们不需要从Base 转换为Derived。查看“Liskov 替换原则”了解更多信息。

【讨论】:

  • 感谢您的回答。实际上,dynamic_cast 可以用两行代码解决问题,甚至无需使用 clone()。但是,我有不同的Derived&lt;T&gt;,如果我使用 dynamic_cast,我将不得不使用一些 if-else,就像你在代码中所做的那样,我想避免这种情况。此外,类参数 T 是事先不知道的,它们是由用户在最开始时通过前向声明定义的。因此,每次定义不同的 T 时,都必须更改代码(if-else)。我正在寻找一些自动化方法,例如 clone()。
  • @Peter 如果要通过Base 指针删除派生对象,那么Base 的析构函数必须是虚拟的。
  • @ChristopheJ.Ortiz 如果您在运行时不知道Base* 指向Derived&lt;T&gt; 但想要获得Derived&lt;T&gt;*,那么根本没有办法避免if - 其他检查。 c++ 中有一个构造,可以在编译时轻松解决不断变化的类参数,而无需更改代码。这是一个模板。
  • @Chris - 您所要求的实际上是设计损坏的标志。对于用户:是的,需要一个虚拟析构函数。我已经更新了我的帖子以回应这两个问题。
【解决方案2】:

我认为在这种情况下你应该进行强制转换。您可以通过 Google 搜索更多关于 static_Cast 和 reinterpret_cast 的信息。这里有一些参考:How to force implicit conversion in polymorphism, Proper way of casting pointer types

【讨论】:

  • 这个问题确实可以通过 dynamic_cast 来解决。但这不是我想要的,因为必须事先知道要执行dynamic_cast&lt;Derived&lt;T&gt; *&gt; 的类型(派生),正如我的帖子中所解释的那样。如果我从我的vector&lt;Base *&gt; 中随机抽取两个元素,那么它们将是Derived&lt;T1&gt; 类型和Derived&lt;T2&gt; 类型,我不知道。因此,如果我做一个 dynamic_cast,我将不得不做一些 if-else 来检查转换是否正常,这是我想避免的。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2011-07-20
  • 2012-01-07
  • 1970-01-01
  • 1970-01-01
  • 2021-09-09
  • 2013-09-23
相关资源
最近更新 更多