关于结构构造函数和析构函数的行为-C ++ [英] Concerning Struct Constructor and Destructor behavior - C++

查看:106
本文介绍了关于结构构造函数和析构函数的行为-C ++的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我不明白为什么该程序的输出如下.为什么没有编译错误?我以为在尝试构造B时,编译器将找不到名为foo()的函数并报告错误.

I don't understand why the output of this program is as follows. Why isn't there a compilation error? I thought when trying to construct B, the compiler would find no function called foo() and report an error.

#include <iostream>
using namespace std;

struct A{
    int a;
    A(int i=0) : a(i) { cout << "A" << endl; }
    ~A() { cout << "Bye A" << endl; }
    int foo() { return a; }
};
struct B{
    int b;
    B(int i=0) : b(i) { cout << "B" << endl; }
    ~B() { cout << "Bye B" << endl; }
    int bar() { return b; }
};
struct C : B, A {
    C(int i=0) : B(foo()), A(i) {}
};

int main() {
    cout << C(10).bar() << endl;
    return 0;
}

输出:

B
A
0
Bye A
Bye B

通常,我想知道何时存在多重继承,父结构的构造和初始化顺序是什么?我还能在课堂上期待类似的行为吗?

In general, I would like to know when there is multiple inheritance, what is the order in which the parent structs are constructed and initialized? Can I expect a similar behavior in classes too?

任何有关构造函数和析构函数调用顺序的解释都将受到赞赏.

Any explanation regarding the order of constructor and destructor calls is much appreciated.

注意:这不是家庭作业.而且,我已经研究了类似的主题,但是关于此问题没有任何建议.

Note: This is not homework. And, I have researched similar topics but nothing came up regarding this issue.

推荐答案

未定义的行为

您正在通过完全初始化对象之前调用foo来调用未定义的行为.引用C ++标准的12.6.2:

Undefined behavior

You're invoking undefined behavior by calling foo before the object is fully initialized. Quote from 12.6.2 in the C++ standard :

可以为正在构造的对象调用成员函数(包括虚拟成员函数10.3). 同样,正在构造的对象可以是typeid运算符(5.2.8)或dynamic_cast(5.2.7)的操作数.但是,如果这些操作是在 ctor-initializer (或直接调用的函数中)中执行的 或在所有基类的 mem-initializers 完成之前间接地从 ctor-initializer ) 操作的不确定性. [示例:

Member functions (including virtual member functions, 10.3) can be called for an object under construction. Similarly, an object under construction can be the operand of the typeid operator (5.2.8) or of a dynamic_cast (5.2.7). However, if these operations are performed in a ctor-initializer (or in a function called directly or indirectly from a ctor-initializer) before all the mem-initializers for base classes have completed, the result of the operation is undefined. [ Example:

class A {
public:
  A(int);
};

class B : public A {
  int j;
public:
  int f();
  B() : A(f()),       // undefined: calls member function
                      // but base A not yet initialized
          j(f()) { }  // well-defined: bases are all initialized
};

class C {
public:
  C(int);
};

class D : public B, C {
  int i;
public:
  D() : C(f()),       // undefined: calls member function
                      // but base C not yet initialized
          i(f()) { }  // well-defined: bases are all initialized
};

—结束示例]

换句话说,按照标准,这是可以的:

In other words, this would be ok according to the standard :

C(int i=0) : B(), A(i) {
    B::b = foo();
}

这将打印10而不是您得到的0(本来可以是其他任何东西,因为那是未定义的行为).

And this will print 10 instead of the 0 that you got (which could have been anything else, since that was undefined behavior).

撇开这种不确定行为的问题,并解决您的问题,初始化发生的顺序是明确定义的:

Setting aside this matter of undefined behavior, and to address your question, the order in which initialization happens is well-defined :

在非委托构造函数中,初始化按以下顺序进行:

In a non-delegating constructor, initialization proceeds in the following order:

-首先,并且仅对于大多数派生类(1.8)的构造函数,虚拟基类在以下位置初始化 它们在基类的有向无环图的深度优先从左到右遍历时出现的顺序, 其中从左到右"是基类在派生类 base-specifier-list 中的出现顺序.

— First, and only for the constructor of the most derived class (1.8), virtual base classes are initialized in the order they appear on a depth-first left-to-right traversal of the directed acyclic graph of base classes, where "left-to-right" is the order of appearance of the base classes in the derived class base-specifier-list.

-然后,直接基类按照它们出现在 base-specifier-list 中的声明顺序进行初始化. (与 mem-initializers 的顺序无关).

— Then, direct base classes are initialized in declaration order as they appear in the base-specifier-list (regardless of the order of the mem-initializers).

-然后,按照在类定义中声明的顺序初始化非静态数据成员 (同样,无论 mem-initializers 的顺序如何.)

— Then, non-static data members are initialized in the order they were declared in the class definition (again regardless of the order of the mem-initializers).

—最后,执行构造函数主体的 compound-statement .

— Finally, the compound-statement of the constructor body is executed.

[注意:声明顺序受强制执行,以确保销毁基础和成员子对象. 初始化的相反顺序. —尾注]

[ Note: The declaration order is mandated to ensure that base and member subobjects are destroyed in the reverse order of initialization. — end note ]

因此,在您的代码中,初始化顺序为:B(B::b),A(A::a),C().

So, in your code, the initialization order is : B (B::b), A (A::a), C ().

但是,更改此初始化顺序(例如,通过使用struct C : A, B代替struct C : B, A)不会摆脱未定义的行为.在B部分初始化之前调用A::foo仍然不确定,即使A部分已初始化.

As noted in the comments below though, changing this initialization order (by eg. using struct C : A, B instead of struct C : B, A) would not however get rid of the undefined behavior. Calling A::foo before the B part is initialized remains undefined, even if the A part is initialized.

这篇关于关于结构构造函数和析构函数的行为-C ++的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆