Thread: Memory issues in C++ vectors compared to ordinary arrays

  1. #1
    Registered User FortranLevelC++'s Avatar
    Join Date
    May 2013
    Location
    United States
    Posts
    81

    Memory issues in C++ vectors compared to ordinary arrays

    First of all, many thanks for your C++ book that I have been reading. For two decades I have been using C++ as if were Fortran, only to multiply arrays and matrices for numerical computation. I did accumulate nearly two dozens of C++ books, but I have never used any serious data structures. Thanks to the concrete motivation in your book, I am already using more serious capabilities of C++, and the results are very productive.

    I have already started to use vectors quite successfully, but I have the following two questions about the memory management of vectors.

    1) If I understand what I have been reading, in general even the clear() method applied to a vector does not free the memory that is already occupied by the vector, it merely erases its contents. If I am correct, then this is unfortunate, and this is in contrast with the memory taken by an array that can be totally freed by using "delete." What can I do to make sure that this memory aspect of the vector data structure does not cause trouble? At least what happens when the entire program terminates, after the program terminates can the memory of a vector cause problems?

    2) I have written and tested the following code where I am passing a vector whose elements are structures, as an argument to a function that modifies the vector and returns this modified vector. Below, I have listed the entire program that is self-contained and it is working. The code seems to work, but I am surprised because I did not expect the following signature of the calling function to be adequate (surprisingly it is working):

    void tester(vector<Item >& );

    Instead, I was expecting that I should have written something like this (with * for the type inside the vector) as the signature of the calling function:

    void tester(vector<Item * >& );

    The reason I am puzzled is because the elements of the vector are not simple things like int or double, they are structures that may require an additional pointer when the vector is passed as an argument to the function.

    Do you think that there is a hidden problem in my code even though it is actually compiling and executing without error?

    In any case, I can see that even for numerical computation, vector and map data structures can be very useful in order to control the architecture and logical behavior of the program. For this reason I will appreciate if you write a section in this website dedicated to advanced uses of vectors, how to create a vector of functions (I think I know how to create an array of functions, but creating a vector of functions that will change real time seems more difficult), and how to pass a vector of functions as an argument to a function. Also, the possibility of creating a vector whose elements are classes (not just structures, but with each class having its specialized function), to pass these vectors as arguments to a function, can be extremely useful in practical applications. I hope that you can write a chapter on this subject.

    // MY ENTIRE CODE IS LISTED BELOW:

    Code:
    #include <stdio.h>      
    #include <stdlib.h>  
    #include <iomanip>
    #include <fstream>
    #include <iostream>
     #include <cstring>
    #include <sstream>
    #include <string>
    #include <string.h>
    
    #include <vector>
    #include <map>
    
    using namespace std;
    
    
    struct Item{
            string Word;
            int Number;
        };
    
    
    void tester(vector<Item >& );  // Signature of the calling function below
    
    
    int main() 
    {
       vector<Item> Exper;
       Item junk1, junk2, junk3;
       int temp;
    
    
        junk1.Word = "a"; 
        junk1.Number = 0;
        junk2.Word = "b";
        junk2.Number = 1;
        junk3.Word = "c";
        junk3.Number = 2;
        
        
         Exper.push_back(junk1);
        Exper.push_back(junk2);
        Exper.push_back(junk3);
    
    
            cout << "Before tester function is called we get: " << endl;
         
        int iterationCounter =0;
            
        for(vector<Item>::iterator itr=Exper.begin(), end=Exper.end(); itr != end; itr++)
        { 
          // cout << *itr << endl;
          /* std::cout << *itr << endl; // this causes a compiler error maybe because the type Item 
            in the vector is not an integer to point to, but the rest of the code works
             when we use an artificial iteration counter called "iteractionCounter" below: */
          cout << Exper[iterationCounter].Word << " " << Exper[iterationCounter].Number << endl;
          ++iterationCounter;
        }
    
      
        tester(Exper); // calling the test function modifying the vector Exper
    
            cout << "After tester function is called we get: " << endl;
         iterationCounter =0;
         for(vector<Item>::iterator itr=Exper.begin(), end=Exper.end();  itr != end; ++itr )
        {
          cout << Exper[iterationCounter].Word << " " << Exper[iterationCounter].Number << endl;
          ++iterationCounter;
        }
            
        
        cout << "Enter any integer to stop program: " ;
            cin >> temp;
    
    
    }
    
    
    void tester( vector<Item > & s)
    {
        s[0].Word = "mod0";
        s[0].Number = 10;
    }
    Last edited by FortranLevelC++; 05-09-2013 at 12:49 AM.

  2. #2
    Algorithm Dissector iMalc's Avatar
    Join Date
    Dec 2005
    Location
    New Zealand
    Posts
    6,318
    It's true that a vector's clear method does not have to free the memory it occupied and thus reduce the capacity to zero, and indeed many implementations hang on to that memory. However thay still only do so for the lifetime of the vector.

    By calling clear on the vector, you're saying that you want it to become logically empty, and the fact that you called clear rather than simply letting the vector go out of scope and be destroyed, suggests that you probably did so because you plan to re-use the vector again for more items. The implementations that do not free the memory at that point do so because it is highly likely you are soon going to subsequently fill the vector with more objects again. In fact, the best estimate of how many objects you will subsequently fill the vector with, is the amount you had filled it with previously! So they're probably saving your program from having to to any unnecessary freeing and reallocating.

    Rest assured, when the vector is destroyed as it goes out of scope, the memory is still always freed.

    And finally, a trick to force the vector to release its memory, is to swap the vector with a new empty vector. E.g.:
    Code:
    vector<Item>().swap(Exper);
    In most cases you should not feel the need to do this though.
    My homepage
    Advice: Take only as directed - If symptoms persist, please see your debugger

    Linus Torvalds: "But it clearly is the only right way. The fact that everybody else does it some other way only means that they are wrong"

  3. #3
    Registered User FortranLevelC++'s Avatar
    Join Date
    May 2013
    Location
    United States
    Posts
    81
    Quote Originally Posted by iMalc View Post
    And finally, a trick to force the vector to release its memory, is to swap the vector with a new empty vector. E.g.:
    Code:
    vector().swap(Exper);
    In most cases you should not feel the need to do this though.
    Many thanks for suggesting the swap() method.But in my code above, is there any way I can use the actual iterator variable itr as an index for the vector, instead of using the artificial counter variable I have used inside the for loop? Since the iterator is not a simple integer, this might be difficult.
    Last edited by FortranLevelC++; 05-09-2013 at 03:10 AM.

  4. #4
    Registered User
    Join Date
    Aug 2005
    Location
    Austria
    Posts
    1,990
    Quote Originally Posted by FortranLevelC++ View Post
    how to create a vector of functions (I think I know how to create an array of functions, but creating a vector of functions that will change real time seems more difficult), and how to pass a vector of functions as an argument to a function.
    In C++ you typically don't need arrays or vectors of functions, You just create a class or struct that define the functions and call them on an instance of that class. Note: structs or classes dont't necessarily have to contain any data members.

    Also, the possibility of creating a vector whose elements are classes (not just structures, but with each class having its specialized function), to pass these vectors as arguments to a function, can be extremely useful in practical applications.
    There is practically no difference between structs and classes in C++ ( just the default access).

    Kurt

  5. #5
    Registered User
    Join Date
    Aug 2005
    Location
    Austria
    Posts
    1,990
    Quote Originally Posted by FortranLevelC++ View Post
    Many thanks.But in my code above, is there any way I can use the actual iterator variable itr as an index for the vector, instead of using the artificial counter variable I have used inside the for loop? Since the iterator is not a simple integer, this might be difficult.
    if you want the index of the item you can get it like
    Code:
        for(vector<Item>::iterator itr=Exper.begin()); itr != Expr.end(); itr++)
        {
          size_t item_idx = itr - Expr.begin();
          cout << Exper[item_idx].Word << " " << Exper[item_idx].Number << endl;
        }

    if you want to do it with a single call you have to write an operator

    Code:
        ostream & operator << (ostream & str, const item & Expr ) {
            str << Expr.Word << " " << Expr.Number;
            return str;
        }
    and use it like this
    Code:
        for(vector<Item>::iterator itr=Exper.begin(); itr != Expr.end(); itr++)
        {
          cout << *itr << endl;
        }

    Kurt
    Last edited by ZuK; 05-09-2013 at 03:35 AM.

  6. #6
    Registered User FortranLevelC++'s Avatar
    Join Date
    May 2013
    Location
    United States
    Posts
    81
    Quote Originally Posted by ZuK View Post
    In C++ you typically don't need arrays or vectors of functions, You just create a class or struct that define the functions and call them on an instance of that class. Note: structs or classes dont't necessarily have to contain any data members.Kurt
    Many thanks, but I don't fully understand. It may be difficult to define just one class that will give a different function in each instance of the class.Can you give an example for parametrizing the classes in a for loop? It seems that what you are saying is that I would first have to create many classes, and then later create a super-class that will include all these classes, and in order to call the specific function, I would then have to call the sub-class of the super-class.Then how would you write the for loop to call these sub-classes of the super-class? This seems to amount to creating an array or vector of classes.

  7. #7
    Lurking whiteflags's Avatar
    Join Date
    Apr 2006
    Location
    United States
    Posts
    9,613
    if I write a new for loop with iterators for the vector, then, unless I make the vector size zero again before refilling the vector, this will confuse the bounds of the for loop and it will not work. So this idea of using swap will be crucial.
    Not true, because vector makes a distinction between capacity and size. Capacity is how many objects can be held, versus size, which is how many are actually held. The results of vector::clear() are that the contents are destroyed, making vector::size() return 0.

    If you want to reset the capacity too, you can swap(), but nothing you said indicates that you need to do that. And I don't believe there is a way to make the capacity exactly 0; vector is always holding onto at least a bit of memory until it gets destroyed. If that really bugs you, you can make an explicit scope with curly braces instead, but this will result in many unnecessary allocations if the vector is used and reused many times, like by being constructed inside a loop. And if your vector is huge, that's bad news, because the allocator may eventually fail to construct the vector and raise an exception.

    But in my code above, do you have any idea why I am not able to write code to output the integer value of the iterator with the pointer notation *itr as you can see?
    C++ has something called operator overloading, which is used prolifically. Even the iterator you are using now has used it to do dereferencing like a pointer would.

    In the code:
    Code:
    cout << *itr << endl;
    First C++ would dereference the iterator, returning a reference to the Item object. This doesn't compile though, because class Item doesn't have an operator << that interfaces with the cout object's class type which is std::istream. You can still use the iterator to do output, it will just look like this.
    Code:
    cout << (*itr).Word << " " << (*itr).Number << endl;
    or
    cout << itr->Word << " " << itr->Number << endl;
    When you learn more about operator overloading you can make the shorter statement work.

  8. #8
    Registered User
    Join Date
    Aug 2005
    Location
    Austria
    Posts
    1,990
    I guess I have misunderstood. You want to iterate over the functions. Forget my reply.

    Kurt
    Last edited by ZuK; 05-09-2013 at 03:34 AM.

  9. #9
    Registered User FortranLevelC++'s Avatar
    Join Date
    May 2013
    Location
    United States
    Posts
    81
    Thanks!

  10. #10
    Registered User FortranLevelC++'s Avatar
    Join Date
    May 2013
    Location
    United States
    Posts
    81
    Quote Originally Posted by whiteflags View Post
    Not true, because vector makes a distinction between capacity and size. Capacity is how many objects can be held, versus size, which is how many are actually held. The results of vector::clear() are that the contents are destroyed, making vector::size() return 0.
    .
    Many thanks. Initially I wrongly imagined that the vector::clear() method does not set the size of the vector to zero, and that it was merely clearing the elements. Thus the vector::clear() method is sufficient for me, since I won't have to worry that the the vector::begin() and vector::end() methods might give the wrong answer when I refill and use the same vector in another for loop with the new upper and lower bounds.

Popular pages Recent additions subscribe to a feed

Similar Threads

  1. Vectors and memory
    By Matty_Alan in forum C++ Programming
    Replies: 5
    Last Post: 05-22-2010, 08:01 PM
  2. Vectors in Arrays
    By Osiris990 in forum C++ Programming
    Replies: 5
    Last Post: 02-25-2008, 02:48 PM
  3. Arrays vs Vectors
    By swgh in forum C++ Programming
    Replies: 5
    Last Post: 05-04-2006, 02:06 AM
  4. arrays or vectors
    By Geo-Fry in forum C++ Programming
    Replies: 26
    Last Post: 04-17-2003, 07:08 PM
  5. arrays and vectors
    By volk in forum C++ Programming
    Replies: 1
    Last Post: 03-30-2003, 03:45 PM