当我按值传递参数时对象被破坏了?

When the object destructed while I pass the argument by-value?

给定以下代码:

#include <iostream>
using namespace std;
class A {
public:
    A() {
    }
    A(const A& a) {
        cout << "A copy ctor" << endl;
    }
    virtual ~A() {
        cout << "A dtor" << endl;
    }
    virtual void type() const {
        cout << "This is A" << endl;
    }
};
class B: public A {
public:
    virtual ~B() {
        cout << "B dtor" << endl;
    }
    virtual void type() const {
        cout << "This is B" << endl;
    }
};
A f(A a) {
    a.type();
    return a;
}
const A& g(const A& a) {
    a.type();
    return a;
}
int main() {
    A *pa = new B();
    cout << "applying function f:" << endl;
    f(*pa).type();
    cout << "~~~ delete: ~~~" << endl;
    delete pa;
    return 0;
}

我得到以下输出:

applying function f:
A copy ctor
This is A
A copy ctor
This is A
A dtor
A dtor
~~~ delete: ~~~
B dtor
A dtor

但是我有些不明白。可以看出,虽然我们从 f 存在,但那里的对象没有被破坏。为什么它没有被破坏? (毕竟,我们超出了函数的范围,所以它必须被销毁,不是吗?)

注意:我强调了有问题的行(我不明白为什么会按这个顺序出现)

我们转到函数 f,此时,我们调用复制 c'tor 并打印 "A copy ctor" (Ok) ,之后,我们 returns 转到函数 f 并转到到 type() ,然后打印 "This is A" ,现在我们从函数 f 中退出,所以我们调用复制 c'tor ,因此将被打印 "A copy ctor" ,但是,现在,析构函数是什么没有被调用(当我们从函数 f 中逃脱时)..?

我认为输出将是以下内容(根据上面的描述):

applying function f:
A copy ctor
This is A
A copy ctor (escape from type)
A dtor (escape from type)
This is A (at the main)
~~~ delete: ~~~
B dtor
A dtor

C++ 标准允许按值函数参数在函数范围内或调用范围内销毁。

如果在调用范围内,它在完整表达式的末尾被销毁(通常是 ;)。如果在函数内部,构造return值后销毁,销毁自动存储局部变量。

A *pa = new B();

创建了一个 B,具有 A 个子对象基础。

cout << "applying function f:" << endl;
f(*pa)//.type();

创建 *pa 的切片副本作为 f 的参数。输出为 A copy ctor\n.

A f(A a) {
  a.type();
  return a;
}

一个.type。在 A 的实例上调用。请注意,此 A*pa 的副本,但它只是 *pa.

A 部分的副本

输出是 This is A,后跟 A(A&&) 移动构造函数,后跟可选的 A dtor。在你的情况下,你有一个复制 ctor 而不是移动 ctor,所以它被称为。这个 copy/move 不能省略,因为不允许从函数参数中省略。输出为 A copy ctor.

此时,编译器可以选择性地销毁作为 f 参数的 A。您的编译器不会在此处破坏 f 的参数。

f(*pa).type();

f 编辑的临时 A return 现在调用了 .type()。这里没有多态性;方法 A::type() 被直接调用。输出为 This is A.

然后我们到达了完整表达式的结尾。

现在由 f 编辑的临时 return 被销毁,如果它之前没有被销毁,后面是 f 的参数。所以输出是 A dtor.

cout << "~~~ delete: ~~~" << endl;
delete pa;

B对象*pa被销毁,然后回收内​​存。由于 ~A 是虚拟的,因此在 *pa.

上调用了正确的析构函数

输出为 B dtor\nA dtor\n

当函数参数被破坏时的行为是实现定义的:

[expr.call]/4

It is implementation-defined whether the lifetime of a parameter ends when the function in which it is defined returns or at the end of the enclosing full-expression.

在您的特定情况下,实施选择后者。