当对象被破坏的同时我传递参数值吗?

时间:2018-07-09 18:04:40

标签: c++ c++11 inheritance language-lawyer virtual

给出以下代码:

#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;
}

我得到以下输出:

  

应用功能f:
  复制ctor
  这是A
  复制ctor
  这是A
   dtor
   dtor
  ~~~删除:~~~
  兄弟
  dtor

但是我听不懂。可以看出,尽管我们从f存在,但对象没有被破坏。为什么它不被破坏? (毕竟,我们超出了函数的范围,因此必须销毁它,不是吗?)

注意:我强调了有问题的台词(我不明白为什么会按此顺序发生)

我们转到函数f,这时,我们调用副本c'tor并打印“ A copy ctor”(Ok),之后,我们返回函数f并转到type(),然后然后,它打印“ This is A”,现在我们从函数f中退出,所以我们调用了副本c'tor,因此将打印“ A copy ctor”,但是,现在,析构函数没有被调用(当我们逃脱功能f)..?

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

  

应用功能f:
  复印机
  这是A
  复制副本(从type退出)
  干事(从type逃出)
  这是A (主要)
   ~~~删除:~~~
   Bdtor
  干事

2 个答案:

答案 0 :(得分:3)

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

如果在调用范围内,则在完整表达式(通常为;)的末尾销毁它。如果在函数内,则在构造返回值并销毁自动存储本地变量后销毁它。

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的副本,但仅是A的{​​{1}}部分的副本。

输出为*pa,然后是This is A移动ctor,然后是A(A&&)。在您的情况下,您有一个复制ctor,而不是一个移动ctor,因此被称为。无法删除此复制/移动,因为不允许您从函数参数中删除。输出为A dtor

这时,编译器可以选择将A copy ctor的自变量A进行销毁。您的编译器不会破坏f的参数。

f

f(*pa).type(); 返回的临时A现在已被调用f。这里没有多态性。方法.type()被直接调用。输出为A::type()

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

现在,由This is A返回的临时文件将被销毁,如果没有更早地销毁,则由f的参数跟随。因此输出为f

A dtor

cout << "~~~ delete: ~~~" << endl; delete pa; 对象B被销毁,然后内存被回收。由于*pa是虚拟的,因此会在~A上调用适当的析构函数。

输出为*pa

答案 1 :(得分:2)

实现参数破坏时的行为由实现定义:

[expr.call]/4

  

由参数定义的生存期在定义函数返回时还是在封闭式全表达式结束时定义在实现中。

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