【问题标题】:Why are C++ objects destroyed when initialized inside function? What can I do to prevent it?为什么在函数内部初始化时 C++ 对象会被销毁?我能做些什么来防止它?
【发布时间】:2021-09-23 04:56:06
【问题描述】:

这里,当我压栈时,为什么对象被销毁了?

#include <iostream>
#include <stack>

class One
{
private:
        int i;
public:
        One(int i) {this->i = i;}
        ~One() {std::cout << "value " << this->i << " is destroyed\n";}
};

int main()
{
        std::stack<One> stack;
        stack.push(One(1));
        stack.push(One(2));

        std::cout << "Now I'll stop\n";
}

我预计在Now I'll stop 之前看不到任何输出。但我明白了

value 1 is destroyed
value 2 is destroyed
Now I'll stop
value 1 is destroyed
value 2 is destroyed

如果我想阻止它们破坏,我应该怎么做?

【问题讨论】:

  • One(1) 创建一个复制的临时对象。一旦被复制并且push 函数返回临时对象不再需要并且它的生命周期结束并被破坏。
  • 看到标记的重复项中没有表示赞成的答案,我认为这个 Q 不应该作为重复项关闭
  • std::vector 是否使用 push_back 复制对象?部分回答了我的问题。 @JHBonarius 我实际上想做一些复杂的事情,比如在类中存储指针。如果我在int *ip;类中多了一个属性,并将构造函数和析构函数修改为One(int i) {this-&gt;i = i; this-&gt;ip = new int(i); }~One() {std::cout &lt;&lt; "value " &lt;&lt; this-&gt;i &lt;&lt; " is destroyed\n"; free(this-&gt;ip);},我就有了double free的问题。
  • 通过new创建的@Sayan对象必须由delete释放,free仅属于malloc. calloc, realloc

标签: c++ temporary-objects


【解决方案1】:

One(1)One(2)构造两个临时对象,传递给push,然后复制(移动)到stack。临时对象在完整表达式后立即销毁。

如果您想避免构建临时对象,您可以改用emplace

将一个新元素压入栈顶。元素是就地构造的,即不执行复制或移动操作。

例如

stack.emplace(1);
stack.emplace(2);

【讨论】:

  • 您对emplace 的引用显示了第一个声明的C++17 之上的C++11。它完全正确吗?
  • @Wolf Return value 与 C++17 不同。
  • 有趣的是,它似乎不是 C++20 中的 constexpr (或者 cppref 需要更新)。 p0784 应该适用吗?
  • @JHBonarius 你的意思是emplace?我检查了它here 并且似乎是非常数的..
  • 我的错误。我有一种误解,认为在 C++20 中所有容器都是 constexpr。但似乎只有std::vector 是...虽然std::stack 构建在其他容器上,包括std:vector,但大多数是非constexpr。
【解决方案2】:

当您这样做stack.push(One(1)); 时,它会创建一个名为One(1) 的右值的临时对象,然后将其复制到堆栈中。所以在复制之后,临时对象会被销毁。

【讨论】:

    【解决方案3】:

    让我在songyuanyao's answer 中添加一个说明性细节来解决实际问题。

    如果您将copy constructor 添加到类One,您将更容易理解发生了什么以及如何保持创建和销毁之间的平衡。 (对你来说,两个创造物获得 4 次破坏不是很神奇吗?)

    只要您没有定义(或提及)copy constructor,编译器就会为您创建一个具有简单实现的函数,该实现对所有成员进行按位复制。因此,在您的情况下,您观察到的行为可能会令人困惑,但并没有做任何特别糟糕的事情。这当然会随着您添加不仅仅涉及复制的成员而改变,例如指向必须在某处销毁的成员的指针......

    在下面的示例中,我向两个构造函数都添加了输出,并将构造函数主体中的成员分配给initialiser list

    class One
    {
      public:
        One(int rhs): i(rhs) {
            std::cout<< "value " << i << " was created from int\n";
        }
        One(const One& rhs): i(rhs.i) {
            std::cout<< "value " << i << " was created by copy\n";
        }
        ~One() {
            std::cout << "value " << i << " is destroyed\n";
        }
    
      private:
        int i;
    };
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2023-03-21
      • 2015-04-24
      • 2014-07-11
      • 1970-01-01
      • 2016-02-09
      相关资源
      最近更新 更多