简体   繁体   中英

How do I correctly manage the memory of dynamically allocated objects?

I'm building a program that will process some fake book orders, and I think I've run into a problem with how I am allocating my memory.

The program flow starts by opening up a text file with the information about each customer. I tokenize that information in a loop and create Customer objects that I want to insert into a database. Here is the loop I am using:

    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);
    }

I use the database as a way to store the Customer objects, which get modified later on in the program. The point is that these Customer objects will be modified and will stick around in the database indefinitely until the end of the program. The problem arises when I try to clean up the memory allocated for these Customers . My database is set up as a sorted linked list, and this is the function I use to remove Customers from the 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;
 }

My concern is that I am not deleting the Customer objects correctly. I know that in C the exact pointer created by malloc needs to be given back to free , but I'm not sure if the same thing holds in C++ with new and delete . I allocated memory for the Customer in the original loop, but I try to return the memory using a method of another class. Is this a valid way to manage the memory, or is there a better way to do this?

The rules in C++ are a little different: your program needs to make a call of either delete or delete[] operator, depending on the operator that you used to allocate the memory.

  • If you used new to allocate memory for a single object, use delete operator
  • If you used new[] to allocate memory for an array of objects, use delete[] operator

In your case you need to call delete (with no square brackets) on the instance of Customer . This should work fine, because all Customer objects are allocated in a loop and added to a non-shared container. Essentially, your linked list "owns" all your Customer objects. In cases like this, removing of a object from the list should trigger deallocation. In situations when your objects could be used from other places (ie the pointers are shared) removing an item from the list should not de-allocate the object.

Note: this is a very low-level of doing things. C++ library provides ready-made list containers, along with smart pointers that you could use for unique and for shared objects, which greatly simplifies the code.

阅读有关RAII的信息 -资源获取是初始化,并使用智能指针来促进资源管理(尤其是分配的内存)

You're correct about using delete to free up memory allocated with new . I'm assuming that database is of type CusttomerList . In that case your code should work fine as long as you didn't accidentally delete or change any pointers in the list earlier. This is exactly why you should only use "naked new " when you absolutely have to. Instead, you can try `std::unique_ptr' in this case:

while(fgets(stringBuf,100,dbFile))
{

        // ...

        std::unique_ptr<Customer> newEntry
        { new Customer(name,custID,credit,street,state,zip)};

        database.insert(std::move(newEntry));
}

Of course you have to declare your list to be list<unique_ptr<Customer>> (assuming that you use the STL linked list. Then whenever you want to delete an element you call `element.reset()' and the memory is released automatically. One other advantage of this method is that you don't actually have to free up memory at all, since all unique pointers will automatically release the allocated memory whenever they go out of scope (eg by the end of the program). You should read more about smart pointers, because in most cases they make resource management more, well, manageable.

You might also want to put the following line in your main() to detect memory leaks:

_CrtSetDbgFlag ( _CRTDBG_ALLOC_MEM_DF | _CRTDBG_LEAK_CHECK_DF );

Your remove function appears to be correctly deleting the Customer objects, if that's what you're asking about. It doesn't correctly handle the case where the list is empty (ie this->getHead() returns null ), though — it should check lead before trying to dereference it in the loop.

You're making things unnecessarily difficult for yourself by managing this linked list manually, however. It'd be much simpler to store the newly-allocated Customer pointer in a std::unique_ptr (assuming you're using C++11) and then store that in a std::list or std::vector . This will take care of automatically deleting the Customer when it's removed from the list (including when the entire list is destroyed).

BTW, the lead->setNext(NULL) in remove is unnecessary. You're changing values in an object that's about to be deleted anyway.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM