具有动态分配的 char 数组的 C++ 结构
C++ struct with dynamically allocated char arrays
我正在尝试将结构存储在向量中。 Struct 需要为给定大小的 char* 动态分配内存。
但是一旦我将结构添加到向量中,它的析构函数就会被调用,就好像我丢失了指向它的指针一样。
为了示例,我制作了这个小演示。
#include "stdafx.h"
#include <iostream>
#include <vector>
struct Classroom
{
char* chairs;
Classroom() {} // default constructor
Classroom(size_t size)
{
std::cout << "Creating " << size << " chairs in a classroom" << std::endl;
chairs = new char[size];
}
~Classroom()
{
std::cout << "Destroyng chairs in a classroom" << std::endl;
delete[] chairs;
}
};
std::vector<Classroom> m_classrooms;
int main()
{
m_classrooms.push_back(Classroom(29));
//m_classrooms.push_back(Classroom(30));
//m_classrooms.push_back(Classroom(30));
system("Pause");
return 0;
}
输出为
Creating 29 chairs in a classroom
Destroyng chairs in a classroom
Press any key to continue . . .
Destroyng chairs in a classroom
是的,好像析构函数被调用了两次!一次添加到矢量,第二次在程序完成执行时。
当我尝试使用 class 而不是结构时,会发生完全相同的事情。
谁能解释为什么会这样,以及正确完成任务的可能方法是什么?
@LPVOID
使用 emplace_back(..) 就地创建对象可以帮助您避免此处遇到的 double free or corruption
错误。
m_classrooms.emplace_back(29)
但是,更好的做法是始终遵循 3/5/0 规则,以免以悬挂指针结束。
Classroom
class 不能安全地用在 std::vector<Classroom>
中,因为它的复制语义不正确。 std::vector
将复制您的对象,如果复制语义有错误,那么当您开始在诸如 vector
的容器中使用 class 时,您会看到所有这些错误都显现出来.
为了使您的 class 具有正确的复制语义,它需要能够无错误地构建、分配和销毁自身的副本(这些错误包括内存泄漏、对同一对象的双重删除调用等)指针等)
您的代码中缺少的另一件事是 size
参数需要在 class 中已知。现在,您发布的只是内存分配,但没有任何东西可以保存 size
。在不知道分配了多少字符的情况下,将无法正确实现用户定义的复制构造函数和赋值运算符,除非 char *
是一个空终止字符串。
话虽如此,有多种方法可以修复您的 class。最简单的方法是简单地使用内置了正确复制语义的类型,而不是自己处理原始动态内存。那些 classes 将包括 std::vector<char>
和 std::string
。他们不仅自己清理,这些 classes 知道自己的大小,而无需携带 size
成员变量。
struct Classroom
{
std::vector<char> chairs;
Classroom() {} // default constructor
Classroom(size_t size) : chairs(size)
{
std::cout << "Creating " << size << " chairs in a classroom" << std::endl;
}
};
上面的 class 不需要任何进一步的调整就可以工作,因为 std::vector<char>
已经有了正确的复制语义。请注意,不再需要析构函数,因为 std::vector
知道如何自我销毁。
如果出于某种原因您必须使用原始动态分配的内存,那么您的 class 必须实现用户定义的复制构造函数、赋值操作和析构函数。
#include <algorithm>
struct Classroom
{
size_t m_size;
char* chairs;
// Note we initialize all the members here. This was a bug in your original code
Classroom() : m_size(0), chairs(nullptr)
{}
Classroom(size_t size) : m_size(size), chairs(new char[size])
{}
Classroom(const Classroom& cRoom) : m_size(cRoom.m_size),
chairs(new char[cRoom.m_size])
{
std::copy(cRoom.chairs, cRoom.chairs + cRoom.m_size, chairs);
}
Classroom& operator=(const Classroom& cRoom)
{
if ( this != &cRoom )
{
Classroom temp(cRoom);
std::swap(temp.m_size, m_size);
std::swap(temp.chairs, chairs);
}
return *this;
}
~Classroom() { delete [] chairs; }
};
注意初始化class成员时成员初始化列表的用法。还要注意在实现赋值运算符时 copy / swap idiom 的用法。
已更正的另一个问题是您的默认构造函数未初始化所有成员。因此,在您原来的 class 中,一个简单的单行程序例如:
int main()
{
Classroom cr;
}
会导致问题,因为在析构函数中,您会删除未初始化的 chairs
指针。
在此之后,std::vector<Classroom>
现在应该可以安全使用了。
我正在尝试将结构存储在向量中。 Struct 需要为给定大小的 char* 动态分配内存。 但是一旦我将结构添加到向量中,它的析构函数就会被调用,就好像我丢失了指向它的指针一样。
为了示例,我制作了这个小演示。
#include "stdafx.h"
#include <iostream>
#include <vector>
struct Classroom
{
char* chairs;
Classroom() {} // default constructor
Classroom(size_t size)
{
std::cout << "Creating " << size << " chairs in a classroom" << std::endl;
chairs = new char[size];
}
~Classroom()
{
std::cout << "Destroyng chairs in a classroom" << std::endl;
delete[] chairs;
}
};
std::vector<Classroom> m_classrooms;
int main()
{
m_classrooms.push_back(Classroom(29));
//m_classrooms.push_back(Classroom(30));
//m_classrooms.push_back(Classroom(30));
system("Pause");
return 0;
}
输出为
Creating 29 chairs in a classroom
Destroyng chairs in a classroom
Press any key to continue . . .
Destroyng chairs in a classroom
是的,好像析构函数被调用了两次!一次添加到矢量,第二次在程序完成执行时。
当我尝试使用 class 而不是结构时,会发生完全相同的事情。
谁能解释为什么会这样,以及正确完成任务的可能方法是什么?
@LPVOID
使用 emplace_back(..) 就地创建对象可以帮助您避免此处遇到的 double free or corruption
错误。
m_classrooms.emplace_back(29)
但是,更好的做法是始终遵循 3/5/0 规则,以免以悬挂指针结束。
Classroom
class 不能安全地用在 std::vector<Classroom>
中,因为它的复制语义不正确。 std::vector
将复制您的对象,如果复制语义有错误,那么当您开始在诸如 vector
的容器中使用 class 时,您会看到所有这些错误都显现出来.
为了使您的 class 具有正确的复制语义,它需要能够无错误地构建、分配和销毁自身的副本(这些错误包括内存泄漏、对同一对象的双重删除调用等)指针等)
您的代码中缺少的另一件事是 size
参数需要在 class 中已知。现在,您发布的只是内存分配,但没有任何东西可以保存 size
。在不知道分配了多少字符的情况下,将无法正确实现用户定义的复制构造函数和赋值运算符,除非 char *
是一个空终止字符串。
话虽如此,有多种方法可以修复您的 class。最简单的方法是简单地使用内置了正确复制语义的类型,而不是自己处理原始动态内存。那些 classes 将包括 std::vector<char>
和 std::string
。他们不仅自己清理,这些 classes 知道自己的大小,而无需携带 size
成员变量。
struct Classroom
{
std::vector<char> chairs;
Classroom() {} // default constructor
Classroom(size_t size) : chairs(size)
{
std::cout << "Creating " << size << " chairs in a classroom" << std::endl;
}
};
上面的 class 不需要任何进一步的调整就可以工作,因为 std::vector<char>
已经有了正确的复制语义。请注意,不再需要析构函数,因为 std::vector
知道如何自我销毁。
如果出于某种原因您必须使用原始动态分配的内存,那么您的 class 必须实现用户定义的复制构造函数、赋值操作和析构函数。
#include <algorithm>
struct Classroom
{
size_t m_size;
char* chairs;
// Note we initialize all the members here. This was a bug in your original code
Classroom() : m_size(0), chairs(nullptr)
{}
Classroom(size_t size) : m_size(size), chairs(new char[size])
{}
Classroom(const Classroom& cRoom) : m_size(cRoom.m_size),
chairs(new char[cRoom.m_size])
{
std::copy(cRoom.chairs, cRoom.chairs + cRoom.m_size, chairs);
}
Classroom& operator=(const Classroom& cRoom)
{
if ( this != &cRoom )
{
Classroom temp(cRoom);
std::swap(temp.m_size, m_size);
std::swap(temp.chairs, chairs);
}
return *this;
}
~Classroom() { delete [] chairs; }
};
注意初始化class成员时成员初始化列表的用法。还要注意在实现赋值运算符时 copy / swap idiom 的用法。
已更正的另一个问题是您的默认构造函数未初始化所有成员。因此,在您原来的 class 中,一个简单的单行程序例如:
int main()
{
Classroom cr;
}
会导致问题,因为在析构函数中,您会删除未初始化的 chairs
指针。
在此之后,std::vector<Classroom>
现在应该可以安全使用了。