【问题标题】:A little help to understand the flow execution of a C++ code一点帮助理解 C++ 代码的流程执行
【发布时间】:2016-12-20 11:27:00
【问题描述】:

我正在阅读来自网站C++ 编程语言C++ Super-FAQ 中构造函数初始化的顺序。那里提供了以下代码。

#include <iostream>
class Y {
public:
  Y();
  void f();
};
Y::Y()      { std::cout << "Initializing Y\n"; }
void Y::f() { std::cout << "Using Y\n"; }
class X {
public:
  X(Y& y);
};
X::X(Y& y) { y.f(); }
class Z {
public:
  Z();
protected:
  X x_;
  Y y_;
};
Z::Z() 
  : y_()
  , x_(y_)
{ }
int main()
{
  Z z;
  return 0;
}

此代码的打印顺序是:

使用 Y

初始化 Y

好吧,我只是无法意识到这种打印顺序是如何可能的,因为在 Z 类的构造函数中,Y 类的实例 y_ 首先被实例化为实例 x_ X级的就是这样。换句话说,如果首先使用方法 Y::f() 我需要实例化一个肯定会调用它的 Y构造函数和打印例程 std::cout .

【问题讨论】:

    标签: c++ constructor initialization


    【解决方案1】:

    因为X x_class Z 的定义中Y y_ 之前出现x_,所以首先构造和初始化。不管你把成员放在初始化列表Z::Z() : y_(), x_(y_) {}中的什么顺序,x_仍然是首先初始化的。

    由于y_ 的构造函数尚未被调用,一些内部元素(如 vtable)将不会被初始化。实际上在 X 的构造函数中使用这个对象可能会导致段错误。也许这个定义更适合你。

    class Z {
    public:
      Z();
    protected:
      Y y_;
      X x_;
    };
    

    由于x_ 是在y_ 之后定义的,所以y_ 将首先被调用,您可以安全地在x_ 的构造函数中使用它。

    您不妨阅读本文的Initialization order 部分。

    编辑: C++ 重视速度并信任程序员,因此它不会尝试验证给它的参数。您可以将空指针强制转换为引用,然后使用该引用。

    Y* p = 0;
    X x(reinterpret_cast<Y&>(p));
    

    这将编译,如果编译器不需要遵守我们的空指针,它就不会失败。如果您将 f 设为虚拟,或尝试访问任何成员,那么这将导致段错误。

    【讨论】:

    • 如果 x_ 先被构造和初始化,但 x 没有默认构造函数。
    • 这种混淆实际上是当您不遵守初始化列表中的顺序时 gcc 打印警告的原因。
    • 它不使用默认构造函数,它仍然使用你在初始化列表中指定的构造函数。它只是没有按照您编写它的顺序执行。它按照它们定义的顺序执行。
    • 我明白你的意思。然而,在x_的初始化过程中,不知何故,方法Y::f()在没有类Y的实例的情况下被调用,这是不可能的。我对吗?那么,这是怎么发生的呢?
    • 给 f 的对象本质上是垃圾。对象已分配但未初始化。使用 Y 的成员、children 或 vtable 将不会被初始化并会导致未定义的行为,但在此示例中您不使用它们。
    【解决方案2】:

    您的类Z 按此顺序声明了两个成员:

    X x_;
    Y y_;
    

    但是,它们的初始化顺序相反:

    Z::Z()
        : y_()
        , x_(y_)
    { }
    

    您需要注意这一点,例如见this questionthis

    更改声明和初始化的顺序以匹配您所期望的。你有未定义的行为,一些编译会警告你关于 oder 的不匹配。

    您链接到的特定 psot 在评论中说 "// Bad: should have listed x_ before y_" 并指出了重点

    请注意,在初始化 (Y::Y()) 之前使用 y_ (Y::f())。

    【讨论】:

    • 我不认为乱序初始化列表会给出未定义的行为。
    • 我发布的其中一个问题声称它是。我会在别处检查。
    • 但是有一点。方法 (Y::f()) 在没有 Y 实例的情况下使用,并且程序不会中断。这种行为真的很奇怪。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2011-02-21
    • 1970-01-01
    • 1970-01-01
    • 2014-09-23
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多