首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >通过在此指针上放置新位置重新初始化对象的未定义行为

通过在此指针上放置新位置重新初始化对象的未定义行为
EN

Stack Overflow用户
提问于 2018-02-09 13:54:38
回答 2查看 524关注 0票数 7

我在皮奥特帕德列斯基的cppcon上看到一篇报告,它说以下是未定义的行为:

代码语言:javascript
复制
int test(Base* a){
  int sum = 0;
  sum += a->foo();
  sum += a->foo();
  return sum;
}

int Base::foo(){
  new (this) Derived;
  return 1;
}

注意:假设sizeof(Base) == sizeof(Derived)foo是虚拟的。

显然这是不好的,但我感兴趣的是为什么它是UB。我确实理解访问realloced指针的UB,但他说,这是一样的。

相关问题:在直接调用析构函数后的未定义行为?中,它说"ok,如果没有例外“,直接调用(虚拟)析构函数有效吗?new (this) MyClass();会导致UB。(与上述问题相反)

C++正在使用放置新的未定义行为两次构造对象吗?说:

程序可以通过重用对象占用的存储或显式调用具有非平凡析构函数的类类型对象的析构函数来结束任何对象的生存期。对于具有非平凡析构函数的类类型的对象,在对象占用的存储被重用或释放之前,程序不需要显式调用析构函数;但是,如果没有显式调用析构函数,或者如果未使用删除-表达式(5.3.5)释放存储,则不应隐式调用析构函数,并且任何依赖于析构函数产生的副作用的程序都具有未定义的行为。

听起来也没问题。

我在有成员的班级的布置和分配中找到了另一个新的位置描述

如果在对象的生存期结束后,在对象占用的存储被重用或释放之前,在原始对象占用的存储位置创建一个新对象,指向原始对象的指针,引用原始对象的引用,或原始对象的名称,则可以自动引用新对象,并且一旦新对象的生存期启动,就可以使用它来操作新对象,如果:

  • 新对象的存储完全覆盖原始对象占用的存储位置,以及
  • 新对象与原始对象的类型相同(忽略顶级cv-限定符),以及
  • 原始对象的类型不是const限定的,如果是类类型,则不包含任何类型为const限定或引用类型的非静态数据成员,以及
  • 原始对象是T类型的最派生对象,而新对象是T类型的最派生对象(也就是说,它们不是基类子对象)。

这似乎解释了UB。但真的是真的吗?

这不意味着,我不能拥有std::vector<Base>吗?因为我认为由于它的预分配,std::vector必须依赖于placement-new和显式的ctors。第4点要求它是派生最多的类型,而Base显然不是。

EN

回答 2

Stack Overflow用户

发布于 2018-02-09 16:10:02

我相信伊丽莎白·巴雷特·布朗宁说得最好。让我数一数。

  1. 如果Base不能被破坏,我们就无法清理资源。
  2. 如果sizeof(Derived)大于动态类型的this的大小,我们将关闭其他内存。
  3. 如果Base不是Derived的第一个子对象,那么新对象的存储将不完全覆盖原始存储,并且您还会破坏其他内存。
  4. 如果Derived只是一个与初始动态类型不同的类型,即使它的大小与我们在不能使用上调用的引用新对象的对象相同。如果BaseDerived的任何成员都是const限定的或是引用,情况也是如此。您需要std::launder任何外部指针/引用。

但是,如果sizeof(Base) == sizeof(Derived)Derived是可销毁的,那么BaseDerived的第一个子对象,实际上只有Derived对象.这很好。

票数 3
EN

Stack Overflow用户

发布于 2018-02-09 14:42:22

关于你的问题

...Because我认为,由于它的预分配std::向量必须依赖于布局-新闻和显式的ctors。第4点要求它是基显然不是的最派生的类型,而第4点要求它是基显然不是的派生最多的类型。

我认为误解来自“派生对象最多”或“最派生类型”一词:

类类型对象的“大多数派生类型”是实例化对象的类,不管该类是否有进一步的子类。考虑以下方案:

代码语言:javascript
复制
struct A {
    virtual void foo() { cout << "A" << endl; };
};

struct B : public A {
    virtual void foo() { cout << "B" << endl; };
};

struct C : public B {
    virtual void foo() { cout << "C" << endl; };
};

int main() {

    B b;  // b is-a B, but it also is-an A (referred  to as a base object of b).
          // The most derived class of b is, however, B, and not A and not C.
}

现在创建vector<B>时,该向量的元素将是类B的实例,因此元素的最派生类型总是B,而不是C (或Derived)。

希望这能带来一些启示。

票数 2
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/48707481

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档