我正在构建一个处理一些假书订单的程序,我想我遇到了如何分配内存的问题。
程序流程首先打开一个包含每个客户信息的文本文件。我在循环中标记该信息并创建我想要插入数据库的Customer
个对象。这是我正在使用的循环:
while(fgets(stringBuf,100,dbFile))
{
string name(strtok(stringBuf,"|"));
int custID = atoi(strtok(NULL,"|"));
double credit = atof(strtok(NULL,"|"));
string street(strtok(NULL,"|"));
string state(strtok(NULL,"|"));
string zip(strtok(NULL,"|"));
Customer *newEntry = new Customer(name,custID,credit,street,state,zip);
database.insert(newEntry);
}
我使用database
作为存储Customer
对象的方法,这些对象稍后会在程序中修改。关键是这些Customer
对象将被修改,并将无限期地保留在数据库中,直到程序结束。当我尝试清理为这些Customers
分配的内存时,会出现问题。我的database
被设置为已排序的链接列表,这是我用来从Customers
删除database
的函数:
void CustomerList::remove(int ID)
{
Customer *lead = this->getHead();
Customer *tail = NULL;
while(lead->getID() != ID) {
tail = lead;
lead = lead->getNext();
}
if(tail == NULL) { // means that lead is pointing to head
this->setHead(lead->getNext());
lead->setNext(NULL);
delete lead;
return;
}
else if(lead == NULL) { // element not found in the list
cout << "Element to be removed was not found in the list\n";
return;
}
else {
tail->setNext(lead->getNext());
lead->setNext(NULL);
delete lead;
return;
}
我担心的是我没有正确删除Customer
个对象。我知道在C中,malloc
创建的确切指针需要返回给free
,但我不确定C ++中是否存在new
和{{1}的相同内容}}。我在原始循环中为delete
分配了内存,但我尝试使用另一个类的方法返回内存。这是管理内存的有效方法,还是有更好的方法来做到这一点?
答案 0 :(得分:3)
C ++中的规则略有不同:您的程序需要调用delete
或delete[]
运算符,具体取决于您用于分配内存的运算符。
new
为单个对象分配内存,请使用delete
运算符new[]
为对象数组分配内存,请使用delete[]
运算符在您的情况下,您需要在delete
的实例上调用Customer
(没有方括号)。这应该可以正常工作,因为所有Customer
对象都在循环中分配并添加到非共享容器中。从本质上讲,您的链接列表&#34;拥有&#34;所有Customer
个对象。在这种情况下,从列表中删除对象应该触发重新分配。在可以从其他地方使用对象的情况下(即指针是共享的),从列表中删除项目不应该取消分配对象。
注意:这是一个非常低级别的服务。 C ++库提供现成的列表容器,以及可用于唯一和共享对象的智能指针,这极大地简化了代码。
答案 1 :(得分:2)
了解 RAII - 资源获取是初始化并使用智能指针它们促进资源管理(特别是已分配的内存)
答案 2 :(得分:2)
使用delete
释放分配了new
的内存是正确的。我假设database
的类型为CusttomerList
。在这种情况下,只要您不小心delete
或更改列表中的任何指针,您的代码就可以正常工作。这正是你绝对必须使用“裸new
”的原因。相反,在这种情况下你可以尝试`std :: unique_ptr':
while(fgets(stringBuf,100,dbFile))
{
// ...
std::unique_ptr<Customer> newEntry
{ new Customer(name,custID,credit,street,state,zip)};
database.insert(std::move(newEntry));
}
当然你必须将你的列表声明为list<unique_ptr<Customer>>
(假设你使用STL链表。然后每当你想删除一个元素时,你调用`element.reset()'并释放内存这种方法的另一个优点是你根本不需要释放内存,因为所有唯一指针会在超出范围时自动释放分配的内存(例如,在程序结束时)。应该阅读更多关于智能指针的内容,因为在大多数情况下,它们可以使资源管理更加,更好,更易于管理。
您可能还想在main()
中添加以下行来检测内存泄漏:
_CrtSetDbgFlag ( _CRTDBG_ALLOC_MEM_DF | _CRTDBG_LEAK_CHECK_DF );
答案 3 :(得分:2)
您的remove
功能似乎正在正确删除Customer
个对象,如果这是您正在询问的内容。它没有正确处理列表为空的情况(即this->getHead()
返回null
),但它应该在尝试在循环中取消引用之前检查lead
。 / p>
但是,通过手动管理此链接列表,您可能会为自己造成不必要的困难。将新分配的Customer
指针存储在std::unique_ptr
中(假设您正在使用C ++ 11),然后将其存储在{{1}中,这样会简单得多}或std::list
。当从列表中删除std::vector
时(包括整个列表被销毁时),这将自动删除Customer
。
顺便说一句,lead->setNext(NULL)
中的remove
是不必要的。您无论如何都要更改即将删除的对象中的值。