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

时间:2014-07-19 18:42:54

标签: c++ memory-management

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

程序流程首先打开一个包含每个客户信息的文本文件。我在循环中标记该信息并创建我想要插入数据库的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分配了内存,但我尝试使用另一个类的方法返回内存。这是管理内存的有效方法,还是有更好的方法来做到这一点?

4 个答案:

答案 0 :(得分:3)

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

  • 如果您使用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是不必要的。您无论如何都要更改即将删除的对象中的值。