为什么这会两次创建Data对象?

时间:2011-05-21 22:58:12

标签: c++

我最近遇到了以下问题,遗憾的是,我无法理解为什么在以下代码中创建了两次Data对象。

我认为一旦_data = i在Base(_data = i)中执行,它将在Derived被销毁之前可用,但正如输出所示,情况并非如此。

希望能够帮助我理解这个概念的逻辑解释。

struct Data
{
    Data(int i_ = 0 ) : _i(i_) { cout << "Inside Data::ctor" << endl; }
    ~Data() { cout << "Inside Data::dtor" << endl ; }

    int _i;
};

struct Info
{
    Info(Data* d_)
    {
            cout << "Inside Info::ctor" << endl;
            cout << d_->_i << endl ;
            d_->_i = 1;
    }

};

struct Base
{
    Base(const Data& data_)
    {
            cout << "Inside Base::ctor" << endl;
            cout << data_._i << endl;
    }
};

struct Derived : public Base
{
    Derived(int i_) : _info(&_data), Base(_data = i_)
    {
            cout << "Inside Derived::ctor" << endl;
            cout << _data._i << endl ;
    }

    Data _data;
    Info _info;
};

int main()
{
    Derived d(100);

    return 0;
}

输出是:

Inside Data::ctor
Inside Base::ctor
100
Inside Data::dtor
Inside Data::ctor
Inside Info::ctor
0
Inside Derived::ctor
1
Inside Data::dtor

2 个答案:

答案 0 :(得分:2)

小心点:

Derived(int i_) : _info(&_data), Base(_data = i_)

编译器可能会生成一个您忽略的警告 忽略警告,它们是代码中的逻辑错误。

构造函数将始终按以下顺序调用构造函数:

  • 首先调用Base()作为基础构造函数:
  • data()首先声明
  • info()这将始终被调用。

这部分:

Base(_data = i_)

实际上正在调用 UN - 初始化成员的赋值,因为i_是一个整数,它在赋值之前首先调用data()的默认构造函数(因此生成第一个print语句)。 / p>

这部分:

info(&_data)

在这里,您传递的是技术上未初始化的对象的地址(尽管它是非法初始化的(如上所述))。虽然传递未初始化对象的地址可能不是非法的,但这是一个坏主意,因为您传递它的对象也不知道它没有被正确初始化,因此任何用法都是UB。

所以你的输出描述了:

Inside Data::ctor         // _data = i_; use i to create temporary object to assign to data.    
Inside Base::ctor         // Base Class constructor
100
Inside Data::dtor         // The temporay object (see above) is destroyed
Inside Data::ctor         // The member _data is initialized
Inside Info::ctor         // The member _info is initialized

答案 1 :(得分:0)

也许在初始化列表中使用成员变量并不是一个好主意。我想首先调用Base的构造函数,它会得到某种临时Data,因为_data尚未构造。然后在从Base::Base返回(并且销毁临时Data)后,_data最终被构造,使用它的默认构造函数(因为它没有在初始化列表中列出,至少不是构造函数语法)。整个问题是Base(_data=i_),因为_data尚未构建。我想首先调用基类的构造函数,然后调用成员变量。请记住,对象不是按照它们在初始化列表中出现的顺序构建的,而是按顺序声明它们。