在基础构造函数完成之前传递`this`:UB还是危险的?

bit*_*ask 10 c++ constructor this undefined-behavior

考虑这个最小的例子(我能想到):

struct Bar;

struct Foo {
  Bar* const b;
  Foo(Bar* b) : b(b) {}
};

struct Bar {
  Foo* const f;
  Bar(Foo* f) : f(f) {}
};

struct Baz : Bar {
  Baz() : Bar(new Foo(this)) {}
};
Run Code Online (Sandbox Code Playgroud)

当传递this给ctor时Foo,没有Baz创建层次结构中的任何内容,但是它们既Foo没有Bar收到也没有任何问题.

现在的问题是,this以这种方式放弃或者未定义的行为是否危险?

问题2:如果Foo::Foo(Bar*)Foo::Foo(Bar&)用相同的语义?我必须通过*this,但deref运算符在这种情况下不会做任何事情.

Cat*_*lus 8

这不是UB.该对象可能尚未正确初始化(因此可能无法立即使用它),但是存储指针以供以后使用很好.

我必须传递*this,但deref运算符在这种情况下不会做任何事情.

当然会,它会取消引用指针.请记住,初始化与分配不同 - 当构造函数运行时,对象已经正确分配(否则您将无法初始化它) - 即它存在,但它在构造函数完成之前处于不确定状态.

  • @bitmask它**引用**指针.你几乎什么都不称呼.在任何体面的编译器上,生成的代码将与您编写的代码完全不同,因此您应该忽略它. (2认同)

Jam*_*lis 5

行为不是不确定的,也不一定是危险的.

既不Foo也不Bar做任何事情与他们收到的指针问题.

这是关键:您只需要知道指针指向的对象尚未完全构造.

如果Foo::Foo(Bar*)Foo::Foo(Bar&)用相同的语义?

就危险性或定义性而言,两者之间确实没有区别.


Gen*_*yev 5

这个问题直接在C++标准3.8/5中得到了解答:

在对象的生命周期开始之前但是在对象将占用的存储之后,或者在对象的生命周期结束之后以及在重用或释放对象占用的存储之前,任何指向存储的指针之前可以使用对象将位于或位于的位置,但仅限于有限的方式.对于正在建造或销毁的物体,见12.7.否则,这样的指针指的是已分配的存储(3.7.4.2),并且使用指针就像指针的类型为void*一样,是明确定义的.这样的指针可以被解除引用,但是所得到的左值可以仅以有限的方式使用,如下所述.如果出现以下情况,该程

  • 对象将是或具有非平凡析构函数的类类型,并且指针用作delete-expression的操作数,
  • 指针用于访问非静态数据成员或调用对象的非静态成员函数,或
  • 指针被隐式转换(4.10)到指向基类类型的指针,或
  • 指针用作static_cast(5.2.9)的操作数(当转换为void*,或者void*以及随后的char*或unsigned char*时除外),或者
  • 指针用作dynamic_cast(5.2.7)的操作数.

另外,在12.7/3中:

显式或隐式地将引用类X的对象的指针(glvalue)转换为指向X的直接或间接基类B的指针(引用),构造X及其所有直接或间接基础的构造直接或间接从B派生的应该已经开始并且这些类的销毁不应该完成,否则转换会导致不确定的行为.