【问题标题】:How can a derived C++ class clone itself via a base pointer?派生的 C++ 类如何通过基指针克隆自身?
【发布时间】:2010-06-28 22:35:33
【问题描述】:

这是我正在尝试做的(此代码不起作用):

class Base
{
    virtual Base *clone() { return new Base(this); }
    virtual void ID() { printf("BASE");
};

class Derived : publc Base
{
    virtual Base *clone() { return new Derived(this); }
    virtual void ID() { printf("DERIVED"); }
}

.
.
Derived d;
Base *bp = &d;
Base *bp2 = bp->clone();

bp2->ID();

喜欢的是看到“DERIVED”打印出来......我得到的是“BASE”。我是一名长期的 C 程序员,并且在 C++ 方面经验丰富……但我在这方面没有取得任何进展……任何帮助将不胜感激。

【问题讨论】:

  • 显示更多代码,特别是在这种情况下每个类的所有构造函数很重要。 (有些可以从你的代码中推断出来,但它有助于从每个人那里获得更正确的答案......)
  • 嗯。初始代码有 Base bp = &d。现在改为 Base* bp = &d;
  • 与问题无关,但您还需要类上的虚拟析构函数。
  • 仍然无法编译,因为没有适合 new Base(this)new Derived(this) 的构造函数
  • 可能重复 [我可以将指针传递给超类,但创建子类的副本吗? ](stackoverflow.com/questions/3063534/…)

标签: c++ cloning


【解决方案1】:

一旦所有的编译错误都得到修复,我就得到了这样的结果:

#include <cstdio>

class Base
{
  public:
    Base() {}
    Base(const Base&) {}
    virtual Base *clone() { return new Base(*this); }
    virtual void ID() { printf("BASE"); }
};

class Derived : public Base
{
  public:
    Derived() {}
    Derived(const Derived&) {}
    virtual Base *clone() { return new Derived(*this); }
    virtual void ID() { printf("DERIVED"); }
};


int main()
{
  Derived d;
  Base *bp = &d;
  Base *bp2 = bp->clone();

  bp2->ID();
}

这为您提供了您正在寻找的东西——衍生。

【讨论】:

  • 感谢大家的宝贵时间。研究这个例子,我想我明白了这个问题。
【解决方案2】:

该代码充满了语法错误。也许最重要的是,Derived 没有从 Base 继承。其次,除了语法错误(可能是简单的拼写错误),Base 显然需要一个虚拟析构函数。 clone 方法几乎要求您可以在基指针 (Base*) 上调用 operator delete。

class Base
{
public:
    virtual ~Base() {}
    virtual Base* clone() const { return new Base(*this); }
    virtual void ID() const { printf("BASE"); }
};

class Derived: public Base
{
public:
    // [Edit] Changed return type to Derived* instead of Base*.
    // Thanks to Matthieu for pointing this out. @see comments below.
    virtual Derived* clone() const { return new Derived(*this); }
    virtual void ID() const { printf("DERIVED"); }
};

int main()
{
    Derived d;
    Base* bp = &d;

    Base* bp2 = bp->clone();
    bp2->ID(); // outputs DERIVED as expected
    delete bp2;
}

【讨论】:

  • 我向大家道歉...我创建了一个简单的示例并发布它,试图避免将每个人都埋在实际代码中...但是我在简单版本中犯了几个错误,并且在我可以之前回来纠正他们,我有几个答案!在这一点上,我不知道哪些答案正在回答我的错误示​​例,这仍然与我的真正问题有关......
  • 使用智能指针(如boost::shared_ptr)自动删除bp2也比较好。
  • @Nathan 这不完全正确。根据 Sutter,无论子类是否具有非 POD 作为成员,通过多态基指针删除都会调用未定义的行为。基类 dtor 应该是公共的和虚拟的,或者是受保护的和非虚拟的,以避免这种情况。当然,当 Derived 不存储 POD 并且没有自己的析构函数时,我们可能能够侥幸逃脱,但它仍然调用未定义的行为。如果 Derived 定义自己的 dtor,则通过 Base* 删除将无法调用该函数。
  • 由于基类本质上是使用克隆方法实现原型模式,因此代码几乎需要我们有一个公共虚拟 dtor,而不管目前如何实现子类。否则很容易产生未定义的行为诱导、脆弱的代码,这些代码很危险。
  • @Mattieu Edit 没关系,这对我来说实际上在 MSVC 和 Comeau 上编译得很好。我试图在一行中写出代码时遇到了不匹配的常量。我每天都在学习新东西!那么,我们可以用一个接受/返回 Derived*/Derived& 的函数来覆盖任何接受/返回 Base*/Base& 的函数吗?当我们直接使用 Derived 而不是通过基本 ptr 工作时,这似乎更容易产生更高效的代码。
【解决方案3】:

Base bp = &amp;d;

你已经“切片”了d,所以对于编译器来说,bp 真的只是Base 类型,这就是为什么当你调用bp-&gt;clone() 时编译器调用Base::clone();bp2-&gt;ID() 打印BASE.

Base&amp; bp = d; 会做你想做的事。

【讨论】:

    【解决方案4】:

    你是slicing 中的Base bp = &amp;d; 类(这会从派生的ptr 构造一个新的基础bp。)

    改用Base* bp = &amp;d;。 (即创建一个指向 Derived 对象的 Base 类型的指针。)

    【讨论】:

    • 我没有在 OP 上看到编辑语句(此时),并且代码已经按照您的建议执行(尽管它根本无法编译)。虽然报告的问题确实有切片问题的味道,但发布的代码并没有证明它......编辑:糟糕,我正在查看错误的帖子以获取编辑标记。你是对的,马库斯。很抱歉给您带来麻烦。
    【解决方案5】:

    您的示例不正确,无法编译。特别是这一行:

    Base bp = &d;
    

    这也可能是您的问题的根本原因(您可能正在切片您的对象),但如果没有看到工作代码,我无法确定。

    你还有一个问题是你的两个类不相关(你的意思是写class Derived : public Base?)

    【讨论】:

    • 是的,我知道这段代码不会编译。我在发布它的一分钟内修复了这个错误,但你们很快!请参阅更正后的问题。我不知道“切片”是什么,但我正在查找它......
    • @wanlessv - 在发布问题之前,您应该始终确保您的代码能够编译。在不确切知道您要做什么的情况下,我们无法始终看到问题所在。
    【解决方案6】:

    代码看起来不错,除了愚蠢的语法错别字和缺少的 ctors。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2016-07-11
      • 2012-01-07
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2016-10-06
      相关资源
      最近更新 更多