C++ 带delete[]的析构函数问题
对于我正在编写的一个程序,我编写了一个简单的数组包装器类(想法是它应该是固定大小的。我知道我可以只使用std::vectors) 我在删除数组时遇到了一个问题C++ 带delete[]的析构函数问题,c++,memory,valgrind,new-operator,delete-operator,C++,Memory,Valgrind,New Operator,Delete Operator,对于我正在编写的一个程序,我编写了一个简单的数组包装器类(想法是它应该是固定大小的。我知道我可以只使用std::vectors) 我在删除数组时遇到了一个问题 这些是我的构造器: template <class T> Array<T>::Array(size_t size): m_data(new T[size]), m_size(size) {} template <class T> Array<T>::Array(const std::vect
这些是我的构造器:
template <class T>
Array<T>::Array(size_t size): m_data(new T[size]), m_size(size)
{}
template <class T>
Array<T>::Array(const std::vector<T> &vec): m_size(vec.size())
{
std::allocator<T> a;
m_data = a.allocate(m_size);
for(int i = 0; i<m_size; i++)
{
new (m_data+i) T(vec[i]);
}
}
模板
数组::数组(大小):m_数据(新的t[size]),m_大小(大小)
{}
模板
数组::数组(const std::vector&vec):m_size(vec.size())
{
分配器a;
m_数据=分配(m_大小);
对于(int i=0;i您正在混合new
和delete[]
,这是未定义的行为。std::allocator::allocate
使用运算符new
,而不是运算符new[]
,因此您必须在其上调用delete
由于您正在混合如何分配存储,因此需要跟踪分配存储的方式,以便正确地取消分配存储
通过将std::vector
作为类的数据成员,可以避免所有这些
template <class T>
Array<T>::Array(size_t size): m_data(size) {}
template <class T>
Array<T>::Array(const std::vector<T> &vec): m_data(vec) {}
模板
数组::数组(大小):m_数据(大小){
模板
数组::数组(const std::vector&vec):m_数据(vec){
你不必像向量那样存储大小成员,这是一个好处。你混合了new
和delete[]
,这是一种未定义的行为。std::allocator::allocate
使用操作符new
,而不是操作符new[]
,因此你必须在上面调用delete
由于您正在混合如何分配存储,因此需要跟踪分配存储的方式,以便正确地取消分配存储
通过将std::vector
作为类的数据成员,可以避免所有这些
template <class T>
Array<T>::Array(size_t size): m_data(size) {}
template <class T>
Array<T>::Array(const std::vector<T> &vec): m_data(vec) {}
模板
数组::数组(大小):m_数据(大小){
模板
数组::数组(const std::vector&vec):m_数据(vec){
你可以不必像向量那样存储大小成员。如果没有使用new[]
初始化内存,就不能使用delete[]
进行删除。这是一种(非常非常,非常很少的情况)直接调用对象的析构函数是正确的:
template <class T>
Array<T>::~Array()
{
for(size_t i = 0; i < m_size; i++) {
m_data[i].~T();
}
allocator.deallocate(m_data);
}
- 我还要确保您避免使用
int
来处理任何涉及此数组大小的内容。有一种说法是,您更喜欢int64\u t
而不是size\u t
(尽管这与STL的行为方式不同,这是一个需要谨慎做出的决定),但您肯定不想限制自己使用带符号的32位数字或更小的数字(在大多数环境中,int
)
关于不使用删除的选择:
<代码>删除< /代码>期望将指针传递给它是它自己的离散分配的对象。使用布局-<代码>新< /代码>创建的对象不按此方式分配。请考虑以下内容:
struct point {
int32_t x, y;
};
int main() {
char memory[1024];
size_t size = 128;
point * arr = reinterpret_cast<point*>(memory);
for(size_t i = 0; i < size; i++) {
new(memory + i) point();
}
for(size_t i = 0; i < size; i++) {
arr[i].x = 5;
arr[i].y = 10;
}
for(size_t i = 0; i < size; i++) {
//undefined behavior, we're deleting objects that weren't allocated on the heap!
delete (arr + (size - i - 1));
}
}
即使在这个内存被分配到堆上的情况下,我们仍然会删除代码没有明确分配的指针。这是语言所禁止的,很容易看出原因:对原始内存的一次释放会擦除分配的整个内存块,不需要释放单个内存块。
struct point {
int32_t x, y;
};
int main() {
char * memory = new char[1024];
size_t size = 128;
point * arr = reinterpret_cast<point*>(memory);
for(size_t i = 0; i < size; i++) {
new(memory + i) point();
}
for(size_t i = 0; i < size; i++) {
arr[i].x = 5;
arr[i].y = 10;
}
for(size_t i = 0; i < size; i++) {
//Still UB: only the first object is a discrete allocation, the rest are part of
//that original allocation. This attempts to deallocate the same chunk of memory 128 times
//delete (arr + (size - i - 1));
//This is correct
arr[size - i - 1].~point();
}
//We do need to deallocate the original memory allocated, but we only do this once.
delete[] memory;
}
结构点{
int32_t x,y;
};
int main(){
字符*内存=新字符[1024];
尺寸=128;
点*arr=重新解释投射(内存);
对于(大小i=0;i
如果未使用new[]
初始化内存,则无法使用delete[]
进行删除。这是一种(非常、非常、非常少数情况)直接调用对象的析构函数是正确的:
template <class T>
Array<T>::~Array()
{
for(size_t i = 0; i < m_size; i++) {
m_data[i].~T();
}
allocator.deallocate(m_data);
}
- 我还要确保您避免使用
int
来处理任何涉及此数组大小的内容。有一种说法是,您更喜欢int64\u t
而不是size\u t
(尽管这与STL的行为方式不同,这是一个需要谨慎做出的决定),但您肯定不想限制自己使用带符号的32位数字或更小的数字(在大多数环境中,int
)
关于不使用删除的选择:
<代码>删除< /代码>期望将指针传递给它是它自己的离散分配的对象。使用布局-<代码>新< /代码>创建的对象不按此方式分配。请考虑以下内容:
struct point {
int32_t x, y;
};
int main() {
char memory[1024];
size_t size = 128;
point * arr = reinterpret_cast<point*>(memory);
for(size_t i = 0; i < size; i++) {
new(memory + i) point();
}
for(size_t i = 0; i < size; i++) {
arr[i].x = 5;
arr[i].y = 10;
}
for(size_t i = 0; i < size; i++) {
//undefined behavior, we're deleting objects that weren't allocated on the heap!
delete (arr + (size - i - 1));
}
}
即使在这个内存被分配到堆上的情况下,我们仍然会删除代码没有明确分配的指针。这是语言所禁止的,很容易看出原因:对原始内存的一次释放会擦除分配的整个内存块,不需要释放单个内存块。
struct point {
int32_t x, y;
};
int main() {
char * memory = new char[1024];
size_t size = 128;
point * arr = reinterpret_cast<point*>(memory);
for(size_t i = 0; i < size; i++) {
new(memory + i) point();
}
for(size_t i = 0; i < size; i++) {
arr[i].x = 5;
arr[i].y = 10;
}
for(size_t i = 0; i < size; i++) {
//Still UB: only the first object is a discrete allocation, the rest are part of
//that original allocation. This attempts to deallocate the same chunk of memory 128 times
//delete (arr + (size - i - 1));
//This is correct
arr[size - i - 1].~point();
}
//We do need to deallocate the original memory allocated, but we only do this once.
delete[] memory;
}
结构点{
int32_t x,y;
};
int main(){
字符*内存=新字符[1024];
尺寸=128;
点*arr=重新解释投射(内存);
对于(大小i=0;i