对象创建和销毁顺序(按C++排列)



我编写了一个简单的程序来了解有关在C++中创建和析构对象的顺序的更多信息(使用Visual Studio 2015)。在这里:

#include <iostream>
#include <string>
using namespace std;
class A
{
public:
    A(string name)
        : name(name)
    {
        cout << "A(" << name << ")::constructor()" << endl;
    }
    ~A()
    {
        cout << "A(" << name << ")::destructor()" << endl;
    }
private:
    string name;
};
class C
{
public:
    C(string name, A a)
        : name(name), a(a)
    {
        cout << "C(" << name << ")::constructor()" << endl;
    }
    ~C()
    {
        cout << "C(" << name << ")::destructor()" << endl;
    }
private:
    string name;
    A a;
};
class B
{
public:
    B(string name)
        : name(name)
    {
        cout << "B(" << name << ")::constructor()" << endl;
    }
    ~B()
    {
        cout << "B(" << name << ")::destructor()" << endl;
    }
private:
    string name;
    A a1{"a1"};
    A a2{"a2"};
    C c1{"c1", a1};
    A a3{"a3"};
};
int main()
{
    B b("b1");
    return 0;
}

输出让我有点惊讶(a1 s):

A(a1)::constructor()
A(a2)::constructor()
C(c1)::constructor()
A(a1)::destructor()
A(a3)::constructor()
B(b1)::constructor()
B(b1)::destructor()
A(a3)::destructor()
C(c1)::destructor()
A(a1)::destructor()
A(a2)::destructor()
A(a1)::destructor()

为了了解有关正在发生的事情的更多信息,我添加了有关对象实例的信息:

    A(string name)
        : name(name)
    {
        cout << "A(" << name << ")::constructor(), this = " << this << endl;
    }
    ~A()
    {
        cout << "A(" << name << ")::destructor(), this = " << this << endl;
    }

结果更令人惊讶:

A(a1)::constructor(), this = 0039FB28
A(a2)::constructor(), this = 0039FB44
C(c1)::constructor()
A(a1)::destructor(), this = 0039F8A8
A(a3)::constructor(), this = 0039FB98
B(b1)::constructor()
B(b1)::destructor()
A(a3)::destructor(), this = 0039FB98
C(c1)::destructor()
A(a1)::destructor(), this = 0039FB7C
A(a2)::destructor(), this = 0039FB44
A(a1)::destructor(), this = 0039FB28

也就是说,为什么a1的构造函数只调用一次,析构函数调用 3 次?我按值传递a,因此显然至少创建了 1 个临时对象,但请向我解释何时以及创建和销毁了多少A实例?

如注释中所述,当您按值将它们作为参数传递时,类型 A 的对象也是通过复制构造构造的。为了看到这一点,你可以自己添加一个复制构造函数:

A(const A& other)
: name(other.name)
{
    cout << "A(" << name << ")::copy-constructor(), this = " << this << endl;
}

示例输出:

A(a1)::constructor(), this = 0xbff3512c
A(a2)::constructor(), this = 0xbff35130
A(a1)::copy-constructor(), this = 0xbff350e8
A(a1)::copy-constructor(), this = 0xbff35138
C(c1)::constructor()
A(a1)::destructor(), this = 0xbff350e8
A(a3)::constructor(), this = 0xbff3513c
B(b1)::constructor()
B(b1)::destructor()
A(a3)::destructor(), this = 0xbff3513c
C(c1)::destructor()
A(a1)::destructor(), this = 0xbff35138
A(a2)::destructor(), this = 0xbff35130
A(a1)::destructor(), this = 0xbff3512c

在线试用

如您所见,当您将 a1 作为参数传递给 c1 的构造函数时,会发生一个复制构造,而当此构造函数初始化其成员 a 时,会发生第二个复制构造。临时副本紧随其后销毁,而成员在 c 销毁时销毁。

编辑:
在这里,您可以阅读创建复制构造函数时的确切规则。
为了不创建默认的复制构造函数,仅仅提供任何用户定义的构造函数是不够的,它需要是一个复制/移动构造函数。

编辑2:

取自 C++14 标准(12.8 复制和移动类对象):

7 如果类定义未显式声明复制构造函数,则隐式声明一个。如果类定义声明移动构造函数或移动赋值运算符,则隐式声明的复制构造函数定义为已删除;否则,它被定义为默认值 (8.4)。如果类具有用户声明的复制赋值运算符或用户声明的析构函数,则不推荐使用后一种情况。

相关内容

  • 没有找到相关文章

最新更新