【问题标题】:When the object destructed while I pass the argument by-value?当我按值传递参数时对象被破坏?
【发布时间】:2018-12-17 11:49:09
【问题描述】:

给出以下代码:

#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
A dtor
A dtor
~~~ 删除:~~~
B dtor
导演

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

注意:我强调了有问题的行(我不明白为什么会以这种顺序发生)

我们转到函数 f,此时,我们调用 copy c'tor 并打印 "A copy ctor" (Ok) ,然后返回函数 f 并转到 type() ,然后然后,它打印 "This is A" ,现在我们从函数 f 中转义,所以我们调用了 copy c'tor ,因此将打印 "A copy ctor" ,但是,现在,没有调用析构函数(而我们逃离函数 f)..?

我认为输出将如下(根据上面的描述):

应用函数 f:
复制 ctor
这是 A
复制 ctor(逃离type)
A dtor(逃离type)
这是A(主要)
~~~ 删除:~~~
B dtor
A dtor

【问题讨论】:

  • 你认为 A dtor A dtor 在你的输出中是什么意思?
  • @NathanOliver 我认为当我们从function f 逃脱时,我们调用了复制c'tor,然后它破坏了对象(在从函数f 逃脱时)在它转到 type() 之后,因此我无法理解输出的顺序。
  • @Software_t 你是说你期望 A copy ctor This is A A copy ctor A dtor 这是 A A dtor
  • 也许如果您分享了您期望的输出,这将有助于澄清您到底在问什么。
  • @Software_t 请发布您期望的输出。您的解释提出的问题多于回答的问题。

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


【解决方案1】:

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 的副本,但它只是*pa 的A 部分的副本。

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

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

f(*pa).type();

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

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

现在 f 返回的临时值被销毁,如果之前没有被销毁,则后面是 f 的参数。所以输出是A dtor。

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

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

输出是B dtor\nA dtor\n。

【讨论】:

  • 你能添加一些我们可以在标准中找到的信息吗?我之前考虑过 nrvo ,但这导致在电源端调用析构函数。这里是 ; .所以对标准的提示会很好。谢谢!
  • 那么我怎么知道会打印什么?
  • @Software_t 标准下有多个合法输出。每个编译器都会决定如何销毁函数参数;他们可以记录或保证。
  • @Yakk-AdamNevraumont 好的,您能否根据我在问题中添加的输出解释一下您认为在我的编译器中销毁函数参数的决定是什么?
【解决方案2】:

函数参数销毁时的行为由实现定义:

[expr.call]/4

参数的生命周期是在定义它的函数返回时结束,还是在封闭的完整表达式结束时结束,这是实现定义的。

在您的具体情况下,实现选择后者。

【讨论】:

    猜你喜欢
    • 2014-08-12
    • 1970-01-01
    • 1970-01-01
    • 2010-12-25
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2018-02-26
    • 2020-11-17
    相关资源
    最近更新 更多