在一种情况下调用构造函数的顺序C++

Order of calling constructors in one case C++

本文关键字:构造函数 调用 顺序 C++ 情况下 一种      更新时间:2023-10-16
#include <iostream>
struct A
{
    A(){std::cout<<"A()"<<std::endl;}
};
template<typename T>
struct B
{
    A a;
    T b;
    B(){std::cout<<"B()"<<std::endl;}
};
int main()
{
    B<B<B<int> > > Test;
    return 0;
}

调用构造函数的 odrer 是

A()
A()
A()
B()
B()
B()

我不知道这是为什么。我以为会是A B A B A B.你能解释一下为什么吗?

这实际上很简单,如果它像 A B A B A B,那么如果你想从 B 的构造函数访问 b,你就会遇到麻烦,因为你认为的顺序意味着第一个成员a被实例化,然后ctor运行,然后b被初始化。实际上,每个成员首先被实例化(构造等(,然后调用构造函数。

这是因为在执行构造函数的主体之前必须初始化成员变量。请考虑以下示例:

struct A {
    int value;
    // Here we explicitly initialize 'value' with 5
    A() : value(5) { }
};
struct B {
    A a;
    B()
    {
        // This is perfectly valid and would print 5,
        // because 'a' has already been implicitly initialized
        // with its default constructor.
        std::cout << a.value;
    }
};

如果不是这种情况,你期望aB的构造函数中有什么价值?你会遇到各种各样的问题。因此,必须在 B() 的主体之前隐式调用 A 的默认构造函数。

从本质上讲,为了使它更明确,这就是正在发生的事情:

    // Initialize 'a' before body of constructor
    B() : a()
    {
        std::cout << a.value;
    }

首先,让我们分析一下你在这里有什么:

  • 你有一个对象Test class B<B<B<int> > > ,它是:

    class B<B<B<int> > > {
        A a;
        B<B<int> > b;
    };
    
  • Test的第二个域,Test.bclass B<B<int> >,即:

    class B<B<int> > {
        A a;
        B<int> b;
    };
    
  • 然后你有第二个Test.b域,Test.b.b,它是class B<int>,它是:

    class B<int> {
        A a;
        int b;
    };
    

所以初始化的顺序是:

  • A() Test.a.
  • A() Test.b.a.
  • A() Test.b.b.a.
  • 没有构造函数,因为Test.b.b.b类型为 int 并且没有构造函数。
  • B<int>() Test.b.b.
  • B<B<int> >() Test.b.
  • B<B<B<int> > >() Test.

不幸的是,所有三个构造函数在输出上都写了同样的东西:B(),但它们是不同类的不同构造函数。

这是预期的行为,因为成员初始化发生在构造函数的主体之前。为了实现这一点,添加成员初始值设定项也很有帮助:

template<typename T>
struct B
{
    A a;
    T b;
    B()
    :
       a(),
       b()
    {
       std::cout<<"B()"<<std::endl;
    }
};

为了完全掌握执行顺序,让我们添加一个虚拟整数字段。我还添加了一个模板来显示嵌套。有关演示,请参阅 http://ideone.com/KylQQb。

#include <cstdio>
struct A
{
    A()
    :
    dummy(printf("Starting to construct A()n"))
    {
        printf("Fully constructed A()n");
    }
    int dummy;
};
template <typename T>
struct Nesting;
template <>
struct Nesting<int>
{
    constexpr static int value = 0;
};
template <template <typename> class T, typename I>
struct Nesting<T<I>>
{
        constexpr static int value = 1 + Nesting<I>::value;
};
template<typename T>
struct B
{
    int dummy;
    A a;
    T b;
    B()
    :
    dummy(printf("Starting to construct B() with nesting %dn", Nesting<B<T>>::value)),
    a(),
    b()
    {
        printf("Fully constructed B() with nesting %dn", Nesting<B<T>>::value);
    }
};
int main()
{
    B<B<B<int>>> Test;
    return 0;
}

其输出将是

Starting to construct B() with nesting 3
Starting to construct A()
Fully constructed A()
Starting to construct B() with nesting 2
Starting to construct A()
Fully constructed A()
Starting to construct B() with nesting 1
Starting to construct A()
Fully constructed A()
Fully constructed B() with nesting 1
Fully constructed B() with nesting 2
Fully constructed B() with nesting 3