提问人:Dov 提问时间:4/28/2023 更新时间:4/28/2023 访问量:79
列表类在析构函数中崩溃,为什么?
list class is crashing in the destructor, why?
问:
以下代码使用 placement new 来移动每个值。 复制构造函数和运算符 = 已被删除,因此分配新内存和复制旧内存的唯一位置是在 add 函数中。
#include <iostream>
#include <string>
using namespace std;
template<typename T>
class DynArray {
private:
T* arr;
uint32_t size;
void* operator new(size_t size, void* loc) {
return loc;
}
public:
DynArray() : arr(nullptr), size(0) {}
~DynArray() {
delete [] arr;
}
DynArray(const DynArray& orig) = delete;
DynArray& operator =(DynArray copy) = delete;
DynArray(DynArray&& orig) : arr(orig.arr), size(orig.size) {
orig.arr = nullptr;
}
void add(const T& v) {
const T* old = arr;
arr = (T*)new char[(size+1)*sizeof(T)];
char* temp = (char*) arr;
for (uint32_t i = 0; i < size; i++, temp += sizeof(T))
new(temp) T(old[i]); // this SHOULD do a move?
new (temp) T(v);
size++;
delete [] (char*)old; // don't call destructors for strings, they are moved
}
friend ostream& operator <<(ostream& s, const DynArray& list) {
for (uint32_t i = 0; i < list.size; i++)
s << list.arr[i] << ' ';
return s;
}
};
DynArray<string> f(int n) {
DynArray<string> ans;
for (int i = 0; i < n; i++)
ans.add("ab");
return ans;
}
int main() {
DynArray<string> c;
c.add("x");
c.add("y");
c.add("zzzz");
cout << c << '\n';
DynArray<string> d = f(3); // calls move constructor
cout << d << '\n';
// code crashes in destructor of d, why>
}
问题的根源是什么?
答:
2赞
Artyer
4/28/2023
#1
您的指针被分配为:错误的类型。delete[]
T*
new char[...]
您必须将其作为其原始类型(即 )删除。delete[] reinterpret_cast<char*>(arr)
在此之前,您必须手动调用每个值的析构函数。移动的对象也必须被摧毁:
~DynArray() {
// Destroy in reverse order of construction
for (T* p = arr + size; p != arr;) {
(--p)->~T();
}
delete [] reinterpret_cast<char*>(arr);
}
void add(const T& v) {
// ...
for (T* p = old + (old_size); p != old;) {
(--p)->~T();
}
delete [] reinterpret_cast<char*>(old);
}
您可能应该将 / 替换为 / 或 /。new char[size]
delete[] ptr
::operator new(size)
::operator delete(ptr)
std::allocator_traits<std::allocator<T>>::allocate(alloc, size)
std::allocator_traits<std::allocator<T>>::deallocate(alloc, ptr, size)
也不动。 会移动。您确实需要考虑异常安全性:如果其中一个构造函数抛出,则需要销毁先前构造的对象。或者,当在新列表中构造对象时,列表本身处于不稳定状态,如果移动构造函数访问列表,则情况很糟糕。您的功能也是不必要的。new(temp) T(old[i])
new(temp) T(std::move(old[i]))
operator new
最后,如果这不是一个学习练习,只需使用std::vector<T>
评论
orig.size=0;
=delete