如何正确管理动态分配对象的内存



我正在构建一个程序来处理一些假书订单,我想我在如何分配内存方面遇到了问题。

程序流首先打开一个包含每个客户信息的文本文件。我在循环中标记该信息,并创建要插入到数据库中的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被设置为排序链表,这是我用来从database中删除Customers的功能:

 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 listn";
            return;
    }
    else {
            tail->setNext(lead->getNext());
            lead->setNext(NULL);
            delete lead;
            return;
 }

我担心的是我没有正确删除Customer对象。我知道在 C 中,malloc创建的确切指针需要返回给free,但我不确定同样的事情是否适用于 newdelete C++。我在原始循环中为Customer分配了内存,但我尝试使用另一个类的方法返回内存。这是管理内存的有效方法,还是有更好的方法?

> C++中的规则略有不同:程序需要调用delete运算符或delete[]运算符,具体取决于用于分配内存的运算符。

  • 如果使用new为单个对象分配内存,请使用delete运算符
  • 如果使用new[]为对象数组分配内存,请使用delete[]运算符

在您的情况下,您需要在 Customer 的实例上调用 delete(不带方括号(。这应该可以正常工作,因为所有Customer对象都在循环中分配并添加到非共享容器中。本质上,您的链表"拥有"您所有Customer对象。在这种情况下,从列表中删除对象应该会触发释放。在可以从其他地方使用对象的情况下(即指针是共享的(,从列表中删除项目不应取消分配该对象。

注意:这是一个非常低层次的做事。C++库提供了现成的列表容器,以及可用于唯一对象和共享对象的智能指针,从而大大简化了代码。

您的remove函数似乎正确删除了Customer对象,如果这是您要询问的。 它不能正确处理列表为空的情况(即 this->getHead() 返回 null (,但是 — 它应该在尝试在循环中取消引用它之前检查lead

但是,通过手动管理此链表,您会给自己带来不必要的困难。 将新分配的Customer指针存储在std::unique_ptr中会简单得多(假设您使用的是 C++11(,然后将其存储在std::liststd::vector中。 这将负责在从列表中删除Customer时(包括销毁整个列表时(自动删除该。

顺便说一句,remove lead->setNext(NULL)是不必要的。 您正在更改无论如何都要删除的对象中的值。

阅读有关

RAII 的信息 - 资源获取是初始化并使用智能指针 它们有助于资源管理(尤其是分配的内存(

您使用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 );

最新更新