【问题标题】:Why so many calls to the destructor ? Does dereference create a copy on the stack?为什么这么多调用析构函数?取消引用会在堆栈上创建一个副本吗?
【发布时间】:2014-06-15 21:55:07
【问题描述】:

这是一段复杂的代码,所以很抱歉没有提供代码 sn-ps。在我的代码中,我在堆上创建了一个对象,如下所示:

{
  State *s = new State();
  ...
  #pragma omp parallel for
  for(int i =0; i < n(blah); ++i) foo(s);
  ...
  delete s;
}  

s 仅被创建一次,但多次传递给函数foo(s)

在函数foo我有

void foo(State *s) {
  State tmp = *s;     // Does this create a copy on the stack ?
  ....
}

我当然可以避免像这样创建tmp,我的动机完全是出于审美(即避免每次都输入s-&gt;bla())。

State tmp = *s 在缓存和别名方面的优缺点是什么?

我在使用g++-4.6 -pg 进行分析时观察到的是,有多次调用State 的析构函数,尽管我主要只创建了一次。

所以我的问题是:取消引用为State tmp = *s; 在堆栈上创建一个副本并在每次范围结束时调用析构函数。

我的预感是赋值运算符被调用以在堆栈上创建对象的副本,然后被销毁。如果是这样,可以避免这种情况

State &tmp = *s;

【问题讨论】:

  • 是的,State tmp = *s; 制作副本。使用参考。
  • 谢谢大家。您还帮助消除了性能瓶颈。此状态对象的复制成本很高。

标签: c++ destructor dereference


【解决方案1】:

是的,正如你所说。我什至不知道你对什么感到困惑。

每当您将variable 声明为T variable; 时,都会创建一个T 实例,可以在variable 的名称下访问。

T *var 不会创建 T 的实例,而是“仅”声明一个指针,而 T&amp; var 是一个引用,它们本质上是相同的,但语义略有不同。

在您的情况下,必须创建 T 的实例。所以无论如何你都会在作用域结束时得到一个析构函数调用。我想说,从这个角度来看,任务并不重要;只是它会复制,因此您需要确保正确编写析构函数。但是,每当您声明类型为 T 的变量(即不是引用、指针或任何花哨的东西)时,当变量的生命周期结束时,总是会在作用域结束时生成对清理代码(析构函数)的调用.

您可以通过查看声明并了解tmp 是什么类型来说明一切。

也可能是一个重要说明。为了保证正确性,如果您切换到引用,您可能应该将tmp 声明为const State &amp;tmp = *s;

以前,foo 中 ... 下的任何内容都会对 tmp 的状态进行任何更改,只会对 *s 的副本进行更改,因此 *s 是“安全的”。现在,如果有任何东西触及tmp,您将在foo 之外看到可见的变化。

【讨论】:

    【解决方案2】:

    您的预感是正确的,函数foo 中的分配将分别创建该对象的副本,然后在移出函数范围时将其销毁。 选择引用确实可以避免复制,在这种情况下,这与直接使用指针非常相似。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2011-11-21
      • 1970-01-01
      • 1970-01-01
      • 2022-10-15
      • 1970-01-01
      • 1970-01-01
      • 2016-03-02
      • 2014-04-22
      相关资源
      最近更新 更多