【问题标题】:How do I make a vector reallocate with out calling a destructor?如何在不调用析构函数的情况下重新分配向量?
【发布时间】:2014-08-03 04:22:02
【问题描述】:

在这段代码中:

Texture * a = new Texture();
Texture * b = new Texture();
buttons.push_back(*a);
buttons.push_back(*b);

buttons 是这样定义的向量:

std::vector<Texture> buttons;

Texture 是一个类,这是它的原型:

class Texture
{
public:
    Picture * texData;
    HitBox * texCoords;
    Texture();
    ~Texture();
    void render_tex();
};

第一个代码块的第 4 行正在调用纹理 a 的析构函数。问题是这个析构函数删除了texDatatexCoords指向的值,这意味着当它被重新分配时texDatatexCoords指向垃圾数据。

有没有办法让向量在重新分配时不会调用析构函数?

【问题讨论】:

  • 这似乎是一个 XY 问题。修复你的类会更好,这样它就可以与向量一起使用,否则不要将它与向量一起使用。
  • 您需要为您的Texture 类定义一个noexcept 移动构造函数。见stackoverflow.com/questions/8001823/…
  • 也定义一个拷贝构造函数和赋值运算符。关注The Rule of Three
  • 为什么析构函数会删除textDatatexCoords?如果对象拥有数据,为什么不是直接数据成员?

标签: c++ class c++11 vector destructor


【解决方案1】:

由于您要动态分配数据成员texDatatexCoords,因此您需要遵循Rule of Three,并同时定义复制构造函数和复制赋值运算符,否则您的代码将很容易被破坏,正如你所发现的那样。

但是有更好的方法!不要使用原始指针作为数据成员。使用unique_ptrs 持有它们。 (您可能根本不需要它们作为指针,但我会在这里为您提供怀疑的好处)。如果您希望类是可复制的,您仍然需要定义一个复制构造函数和复制赋值运算符,并手动复制 unique_ptrs 所持有的内容。

为了使其可移动,您可以显式地 =default 移动构造函数和移动赋值运算符。


另外,您的代码正在泄漏内存。您动态分配这些对象,然后将push_back 复制vector

Texture * a = new Texture();
buttons.push_back(*a);

您正在取消引用 a 并将其副本添加到 vector。除非你在它超出范围之前delete a;,否则你有内存泄漏。请改用vector&lt;unique_ptr&lt;Texture&gt;&gt;

vector<unique_ptr<Texture>> buttons;

buttons.push_back(unique_ptr<Texture>(new Texture()));

使用 C++14,您可以使用 make_unique 来避免自己必须 new 对象

buttons.push_back(make_unique<Texture>());

此外,如果您提前知道要添加的按钮数量,您可以reserve 空格以避免重新分配。

buttons.reserve(num_buttons);

现在,您实际上可能根本不需要动态分配 Texture 对象。您可能需要的只是

vector<Texture> buttons;
buttons.push_back(Texture());

如果可能,我还会为 Texture 定义一个移动构造函数和移动赋值运算符,以便在向量重新分配时可以移动它,而不是被复制。

【讨论】:

  • 好点,但您的示例不是异常安全的。 (我们需要 C++14 快点,所以我们可以使用std::make_shared
  • @BenVoigt 到达那里...等一下:)
  • 谢谢。内存泄漏的原因是我试图找出问题所在,所以这段代码与最优代码有点不同。此外,如果我知道按钮的数量,我可以使用一个数组。我将答案检查给了 Steger,因为他更简洁,但感谢您提供完整的答案
  • @0ctoDragon:从技术上讲,Steger 并没有完全回答这个问题,尽管他的观察肯定是有效的。
  • @BenVoigt std::make_shared 已经在 C++11 中了。 C++14 将带来std::make_unique
【解决方案2】:

使用智能指针而不是原始指针。以下清单使用std::unique_ptr。如果对象有多个所有者,您也可以使用std::shared_ptr

std::vector<std::unique_ptr<Texture>> buttons;

std::unique_ptr<Texture> a{new Texture()};
std::unique_ptr<Texture> b{new Texture()};
buttons.push_back(std::move(a));
buttons.push_back(std::move(b));

【讨论】:

    【解决方案3】:

    这里有一个更大的问题。由于您有一个析构函数,因此您还需要正确实现赋值运算符和复制构造函数。然后您的代码将按原样正常工作。谷歌 c++ 和三法则。如前所述,智能指针可以消除对三巨头的需求,因为它们将在幕后为您处理复制和删除。

    【讨论】:

      【解决方案4】:

      Texture 是否拥有 PictureHitBox?在这种情况下,也许它应该按值或unique_ptr 保存PictureHitBox。瞄准rule of zero 并且根本没有析构函数。另外,动态分配Texture 对象是否有充分的理由?我会建议类似:

      #include <vector>
      
      struct Picture {};
      struct HitBox {};
      
      struct Texture {
        Picture texData;
        HitBox  texCoords;
        void renderTex() { }
      };
      
      int main() {
        std::vector<Texture> textures(2);
      }
      

      【讨论】:

        猜你喜欢
        • 2020-08-14
        • 2019-11-27
        • 1970-01-01
        • 2015-03-13
        • 1970-01-01
        • 2011-03-11
        • 2012-10-04
        • 2017-08-17
        相关资源
        最近更新 更多