【问题标题】:Can I use reinterpret_cast to destroy non dynamically allocated objects?我可以使用 reinterpret_cast 来销毁非动态分配的对象吗?
【发布时间】:2016-01-18 16:34:43
【问题描述】:

我已经做了一个非常简单的vector实现。我承认我作弊很多,唯一的实例变量是 size_t sz 和 T * elem。许多问题之一(但这是我所知道的唯一可能导致崩溃的问题)是pop_back() 的问题。

template <typename T>
    void vec<T>::pop_back() {
    if (sz == 0)
        return;
    elem[sz-1].~T();
    --sz;
}

向量elem 是一个动态分配的数组,但数组中的对象可能是也可能不是。我在这里看到的问题是,如果我销毁这个数组中的一个对象,这可能会崩溃。如果我多次破坏元素,就会发生这种情况。可以这么说,以防我使用了 pop_back。我的编译器似乎没有发生这种情况(这看起来很奇怪),但我听说有人在使用这个函数时遇到了这个问题。我一直在尝试在网上做一些研究,发现另一个alternative。这里没有做零校验(可能是懒惰),本例使用reinterpret_cast

template<class T>
void Vector<T>::pop_back() {

    // You way want to do a check for empty() before calling the destructor.

    // Call the destructor.
    (reinterpret_cast<T*>(buffer)[_size-1]).~T();

    // It is usual to prefer the pre decrement (and pre increment).
    --_size;
}

问题是,我真的可以使用 reinterpret_cast 来销毁向量类中的非动态分配对象吗?

编辑

根据要求,我将展示我的 push_back,我不确定如何在短时间内正确编写它。我承认该类的主要目的不是效率,而是一种处理资源而不是原始动态数组的好方法(我当然可以使用向量,但这是一个关于更小的范围而不是一般策略的问题,所以如果省略了 use-std::vector 讨论)

template <typename T>
void vec<T>::push_back(const T& obj) {
    T* tmp = new T[sz+1];
    if (sz > 0){
        uninitialized_copy(elem, elem+sz, tmp);
        delete[] elem;
    }
    elem = tmp;
    elem[sz] = T{obj};
    ++sz;
}

主要问题是容量。我意识到容量部分需要大量工作,没有它我只能创建新元素。

【问题讨论】:

  • 您引用的示例使用“无类型”char * 缓冲区以避免不必要的 ctor 调用,因此转换为 T*。相比之下,您的数组 elem 已经输入,我接受。并且直接在对象上调用 dtor 与动态分配无关。 (由于该对象似乎驻留在您的 elem 向量中,您应该知道它的分配方式;-)。 )
  • 我会更担心T 没有析构函数的情况。例如int 的向量。如果T 是int 那么var.~int() 没有意义。
  • 您能否详细说明一下您所说的“向量元素是一个动态分配的数组,但数组中的对象可能是也可能不是。”?你的数组究竟是如何分配的,你的对象是如何构造的?
  • 显示你的 push_back()
  • @CrazyEddie: var.~int() 可能没有意义,但如果 T 是一个模板参数,int 就可以了。这只是一个空操作。

标签: c++ reinterpret-cast


【解决方案1】:

这个答案的灵感来自 Herb Sutter 所著的Exceptional C++一书(第 12 项)中的堆栈实现:

你可以为你的vec分配内存,如果你初始化你的T * elem变量

elem = static_cast<T*>(sz == 0 ? nullptr : operator new(sizeof(T)*sz));

这将为您提供尚未构造对象的内存。

编辑(感谢 Cheers 和 hth. - Alf):您也可以通过调用 a.allocate(sz) 来使用分配器而不是上面的代码。

如果要向向量中添加新元素,可以使用construct [1]。要调用对象的析构函数,可以使用destroy[2]。

如果您现在在变量used 中跟踪向量中分配了多少元素,则可以通过调用destroy(elem+used); 取消分配最后一个元素

construct 和 destroy 在其中使用放置 new 和显式析构函数调用。我建议使用这些函数,而不是原始的布局消息和析构函数调用。

【讨论】:

  • 这确实看起来很聪明,并且现在使用分配器作为类的成员似乎是个好主意。感谢您使我看到的许多实现更有意义。
  • 使用像construct和destroy这样的分配器函数似乎不协调,但不使用分配器进行分配。这里有什么想法?
  • @Cheersandhth.-Alf 我们不想使用普通的new,因为我们还不想构造对象。我们大小为 n 的向量将包含 n 个标准构造的对象,我们无论如何都想在以后覆盖。所以我们自己进行分配并显式调用构造函数和析构函数。 construct 和 destructs 只是使用 new (ptr) T(value) 和 ptr-&gt;~T() 的奇特方式。
  • @Otomo:一切都很好,但是你为什么不使用分配器来分配?就像,它被称为“分配器”,因为它,嗯,分配。这就是它的工作,它是客户端代码的自定义点。
  • @Cheersandhth.-Alf 你是对的。我试过了,它也适用于std::allocator a; 和a.allocate(sz);。在课堂上,您必须确保在为 elem 分配内存之前初始化分配器。
猜你喜欢
  • 2010-10-19
  • 2021-12-16
  • 2020-01-10
  • 1970-01-01
  • 2014-01-04
  • 2019-03-01
  • 1970-01-01
  • 1970-01-01
  • 2020-04-06
相关资源
最近更新 更多