繁体   English   中英

为什么具有虚函数的C ++类需要具有非平凡的复制构造函数?

[英]Why are C++ classes with virtual functions required to have a non-trivial copy constructor?

根据C ++标准,具有虚函数的类不能具有普通的复制构造函数:

如果不是用户提供的,则类X的复制/移动构造函数是微不足道的

- 类X没有虚函数(10.3),没有虚基类(10.1),和

- 选择复制/移动每个直接基类子对象的构造函数是微不足道的,并且

- 对于类类型(或其数组)的X的每个非静态数据成员,选择复制/移动该成员的构造函数是微不足道的;

否则复制/移动构造函数是非平凡的。

现在,想象一个类层次结构,它满足除“无虚函数”条件之外的所有上述条件:

struct I
{
    virtual void f() = 0;
};

struct D final : I
{
   void f() override 
   {
   }
};

从实现的角度来看,这些类只包含指向虚拟调度表的指针。 并且基类没有任何用户声明的构造函数,并且派生类是final,因此提到的指针总是可以具有常量值。 鉴于这一切,复制构造函数可以是微不足道的,但标准明确禁止对其进行处理。

另一方面,满足了对诸如可破坏的类别进行处理的条件。 该类不声明虚拟析构函数并使用隐式定义的析构函数。

要求简单的析构函数看起来像强制优化 - 在这种情况下,隐式定义的析构函数不应该恢复指向虚拟表的指针。

但是在我看来,这样的优化是中途的; 具有虚函数的类仍然无法进行memcopied,即使它们可以被轻易破坏。

问题:

  1. 我是否有任何理由从实现角度考虑为什么这些类应该有非平凡的复制构造函数?

  2. 是否有任何理由不能在标准中放宽对复制构造函数的微不足道的限制?

我是否有任何理由从实现角度考虑为什么这些类应该有非平凡的复制构造函数?

有一个非常明显的一点: I复制构造函数并不是微不足道的。 它不是最终的,所以可以有其他派生类。 所以它必须是非平凡的并且在memcpy之后正确设置虚拟表指针,因为可能存在依赖它的派生类。

是否有任何理由不能在标准中放宽对复制构造函数的微不足道的限制?

1)构造函数的琐碎部分根本没有修改,包含final关键字。

2)人们认为像deletefinaloverrride这样的关键词应该有助于避免大多数常见错误,并澄清程序员的意图,而不是改变程序的行为。

3)它使语言变得复杂: 构造函数是微不足道的,除非你有虚函数,然后它是不平凡的,除非你的类是最终的,然后它是微不足道的,除非别的,否则它不是,除非......

4)没有人认为值得为正式文件撰写,证明这一增加对委员会有用并将这种变化推向语言。

多态类的副本和移动不可能是微不足道的,因为它会破坏切片副本和移动,从而复制对象动态类型的基本类型。 例如:

struct Base { virtual int f() { return 0; } };
struct D1 : Base { int f() override { return 1; } };
struct D2 : Base { int f() override { return 2; } };

int main() {
  D1 d1;
  D2 d2;
  Base b = d1; // if this copy constructor were trivial, b would have D1's vtable
  b.f();       // ...and this call would return 1 instead of 0.
  b = d2;      // Ditto: b would have D2's vtable
  b.f();       // ...and this call would return 2 instead of 0.
}

琐碎的构造函数意味着不需要额外的努力。 因此对于复制c / tor / assigment运算符,它意味着简单的memcpy 你提到的虚函数正在创建vtable,它是函数指针表。 因此,如果您尝试复制D对象的内存,您还将复制其vtable。 新对象将具有指向旧内存的指针的vtable。 这将是一个理想的情况。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM