【问题标题】:Can we make a class copy constructor virtual in C++我们可以在 C++ 中创建一个虚拟的类复制构造函数吗
【发布时间】:2012-04-10 23:33:17
【问题描述】:

我们可以在 C++ 中创建一个虚拟的类复制构造函数吗?如何使用?

【问题讨论】:

  • 假设你可以......构造函数调用应该分派到哪个类型?
  • @DavidRodríguez-dribeas:这是一个很好的观点。我认为这应该是一个答案,因为它也解释了理由
  • @DavidRodríguez-dribeas:Luchian 的答案中的 c++faq 链接提供了答案。我不介意直接在语言中使用它,而不必提供 create() 和 clone()(比如在下一个标准中)。
  • @stefaanv, nawaz:我知道成语,评论的意思是让你认为构造函数应用于尚未创建的对象(此时它只分配了内存),并且 C++ 中的调度应用于正在调用该方法的对象(此时只是一个内存块)。习惯用法颠倒了顺序,并在 source 对象上使用虚拟调度,而不是在有效对象的目的地上。目的是让用户思考被问到的内容。由于成语成为标准的一部分,我不会打赌。

标签: c++ constructor virtual


【解决方案1】:

是的,您可以创建虚拟副本构造函数,但不能创建虚拟构造函数。

原因:

虚拟构造函数:- 不可能,因为 c++ 是静态类型语言,并且将构造函数创建为虚拟,因此编译器将无法确定它是什么类型的对象,并且由于 virtual 关键字而将整个过程留给运行时。 编译器必须知道创建对象的类类型。换句话说,从 C++ 编译器的角度来看,要创建什么类型的对象是编译时决定的。如果我们将构造函数设为虚拟,编译器会标记错误。

Virtual Copy 构造函数:- 是的,可以考虑剪贴板应用程序。剪贴板可以保存不同类型的对象,并从现有对象复制对象,将它们粘贴到应用程序画布上。同样,要复制的对象类型是运行时决定。虚拟复制构造函数填补了这里的空白。

【讨论】:

  • 语言是否允许使用虚拟拷贝构造函数与概念是否应该是可能的不同。
【解决方案2】:

您不能,因为在调用构造函数之前分配的内存是基于新类型的大小而不是复制操作数。如果它确实有效,那将是一种特殊情况,即反转许多语言结构的多态性。

但这并不意味着它不能用一点 C++ 魔法来完成。 :)

在某些情况下它非常有用,例如序列化非 POD 类。这个例子创建了一个使用placement new 工作的虚拟拷贝构造函数。

警告:这是一个可以帮助某些用户解决特定问题的示例。不要在通用代码中这样做。如果为新类分配的内存小于派生类,它将崩溃。最好(也是唯一)安全的使用方法是,如果您正在管理自己的类内存并使用新位置。

class VirtualBase
{
public: 
    VirtualBase() {}
    virtual ~VirtualBase() {}

    VirtualBase(const VirtualBase& copy)
    {
        copy.VirtualPlacementCopyConstructor(this);
    }

    virtual void VirtualPlacementCopyConstructor(void*) const {}
};

class Derived :: public VirtualBase
{
public:
    ...

    Derived(const Derived& copy) : ... don't call baseclass and make an infinite loop
    {
    }

protected:
    void VirtualPlacementCopyConstructor(void* place) const
    {
        new (place) Derived(*this);
    }
};

【讨论】:

  • 这不会编译,即使您修复它以使其编译,实现也相当彻底地破坏了。 Derived 的所有成员都将遭受双重初始化。这可能恰好适用于一些简单的类型,但显然会导致未定义的行为。 “构造函数”需要是 void VirtualPlacementCopyConstructor(VirtualBase* place) const { auto d = dynamic_cast<Derived*>(place); if (d) { d->~Derived(); new (d) Derived(*this); } }
  • 现在应该很明显问题出在哪里了:VirtualPlacementCopyConstructor 被调用得太早了,而复制到的类的类型还不是 Derived。因此析构函数也不会做正确的事情,并且你会得到很多未定义的行为。如果您将dynamic_cast 替换为static_cast,那么对于POD 类型来说一切正常,仅此而已。只要您添加例如std::string 成员,东西会坏掉(更糟糕的是:它不会总是坏掉,所以你会把头发拉出来)。
【解决方案3】:

不,你不能,构造函数不能是虚拟的。

C++03 - 12.1 构造函数

4) 构造函数不得为virtual (10.3) 或static (9.4)。 [...]

如果你需要这样的东西,你可以查找虚拟构造函数成语here

【讨论】:

  • 你的链接被禁止
  • 虽然在大多数情况下使用虚拟方法解决它是可以的,但需要指出的是,这些不是构造函数,它是一个抽象工厂。
【解决方案4】:

不,你不能。

此外,整个概念没有意义。虚函数是根据对象的值(对象的动态类型)调度的函数。当调用构造函数时,对象还没有值(因为它还没有被构造)。因此,不可能发生虚拟调度。

考虑一下。这样的构造函数会有什么语义?

【讨论】:

  • 感谢您解释为什么不能创建虚拟构造函数。
【解决方案5】:

没有。 C++ 是静态类型语言,C++ 编译器以多态方式创建对象是没有意义的。编译器必须知道创建对象的类类型。换句话说,从 C++ 编译器的角度来看,要创建什么类型的对象是编译时决定的。如果我们将构造函数设为虚拟,编译器会标记错误。

【讨论】:

  • 不完全正确,参见抽象工厂模式。
【解决方案6】:

永远不会,这在 C++ 中是不可能的。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2011-04-08
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2017-11-13
    相关资源
    最近更新 更多