【问题标题】:Why does my class's destructor get called when I add instances to a vector?当我将实例添加到向量时,为什么会调用我的类的析构函数?
【发布时间】:2012-02-17 16:05:36
【问题描述】:

似乎每次我向向量m_test添加一个对象时,都会调用析构函数方法。我错过了什么吗?我怎样才能防止这种情况发生?

class TEST
{
public:
    TEST();
    ~TEST();
    int * x;
};

TEST::TEST()
{
}

TEST::~TEST()
{
... it is called every time I push_back something to the vector ...
    delete x;
}

    vector<TEST> m_test;
    for (unsigned int i=0; i<5; i++)
    {
        m_test.push_back(TEST());
    }

【问题讨论】:

  • 在 C++11 中,您可以使用 m_test.emplace_back() 来避免创建临时文件。无论如何,请永远记住Rule of Three

标签: c++ stl vector


【解决方案1】:

这里的问题是您违反了Rule of Three。你的类有一个析构函数,所以你也需要一个复制构造函数和一个赋值运算符。或者,您不能允许复制您的类(例如,通过将T(T const&amp;)T&amp; operator=(T const&amp;) 设为私有,或从boost::noncopyable 派生),然后调整向量的大小而不是使用push_back

在第一种情况下,您可以像往常一样 push_back 您的班级。在第二种情况下,语法类似于

std::vector<TEST> vec(5);
// vec now has five default-constructed elements of type TEST.

不做这两件事是个坏主意,因为您很可能在某些时候遇到双重删除问题——即使您认为您永远不会复制或分配TESTx != nullptr,明确禁止它更安全。

顺便说一句,如果您有在对象超出范围时应该删除的成员指针,请考虑使用智能指针,如 scoped_ptrunique_ptrshared_ptr(如果您是无法使用 Boost 或 C++11)。

【讨论】:

  • 谢谢 Anton,我是 C++ 编程新手,从未听说过三法则。感谢您为我指明正确的方向。
  • 你从哪里学来的...?我之前的 CPP 课程完全烂透了……
【解决方案2】:

不叫push_back,叫临时销毁

在您的示例中修复它:

TEST test;
for (int i = 0; i < 5; ++i)
{
    m_test.push_back(test);
}

应该只调用一次。

您的代码正在循环内创建一个临时的TEST,并在push_back 中使用它,然后当循环结束/重复并被销毁时,该临时代码将超出范围。这完全按照应有的方式发生,因为临时的 TEST 需要清理。

如果你想避免这种情况,除了为每次推送创建一个临时对象之外,你还需要做任何其他事情。一种可能的解决方案是:

vector<TEST> m_test(5); // Note reserving space in the vector for 5 objects

std::fill(m_test.begin(), m_test.end(), TEST()); // Fill the vector with the default ctor

根据您的 STL 的优化方式,这可能不需要制作多个副本。

如果您在 TEST 类中实现复制构造函数,您也可以获得更好的处理,例如:

TEST::TEST(const TEST & other)
{
    x = new int(*other.x); // Not entirely safe, but the simplest copy ctor for this example.
}

这是否合适,或者你如何处理它,取决于你的类和它的需要,但是当你定义了你自己的常规构造函数和析构函数时,你通常应该有一个复制构造函数(否则编译器会生成一个,并且在在这种情况下,将导致复制和挂起指向x 的指针)。

【讨论】:

    【解决方案3】:

    为避免破坏临时以避免复制构造函数,请考虑使用vector::resizevector::emplace_back。这是一个使用emplace_back的示例:

    vector<TEST> m_test;
    m_test.reserve(5); 
    for ( uint i=0; i<5; i++ ) 
    {
        m_test.emplace_back();
    }
    

    矢量元素将在原地构造,无需复制。当 vt 被销毁时,每个向量元素都会被自动销毁。

    c++0x 是必需的(将-std=c++0x 与 gnu 一起使用)。 #include &lt;vector&gt; 当然也是必须的。

    如果不使用默认构造函数(例如,如果 TEST::x 是引用而不是指针),只需将参数添加到对 emplace_back() 的调用中,如下所示:

    class TEST
    {
    public:
        TEST( int & arg) : x(arg) {;} // no default constructor
        int & x; // reference instead of a pointer.
    };
    
    . . . 
    
    int someInt;
    
    vector<TEST> m_test;
    m_test.reserve(5);
    for ( uint i=0; i<5; i++ ) {
        m_test.emplace_back( someInt ); // TEST constructor args added here.
    }
    

    显示的reserve() 是可选的,但可确保在开始构造向量元素之前有足够的可用空间。

    【讨论】:

      【解决方案4】:

      vector.push_back() 将给定对象复制到其存储区域。您在 push_back() 调用中构建的临时对象在被复制后立即被销毁,这就是您所看到的。一些编译器可能能够优化这个副本,但你的显然不能。

      【讨论】:

        【解决方案5】:

        m_test.push_back(TEST()); 中,TEST() 将创建一个临时变量。 vector将其复制到自己的内存后,临时变量就被销毁了。

        你可以这样做:

        vector<TEST> m_test(5, TEST());
        

        【讨论】:

          猜你喜欢
          • 2014-03-14
          • 2017-01-18
          • 2021-12-15
          • 2022-01-26
          • 2010-12-21
          • 2014-12-07
          • 1970-01-01
          • 2011-02-17
          • 1970-01-01
          相关资源
          最近更新 更多