You are here:

C++/Please help me with vectors

Advertisement


Question
QUESTION: HI,

I am new to using vectors in C++.
I have initialized 2 vectors "Rel" and "Wed"
Now, both the vectors are in a for loop and the "Wed" vector elements change with for loop and for each time it loops, I want to embed the contents of "Wed" into "Rel" such that at the end of the loop the "Rel" contains all the results of that of "Wed"

How can I do this.

Thanks,
Rahul



ANSWER: If you are the same Rahul that asked the question about string literals a day or so ago then I am going to make a similar point to one I made in my answer to that question (the implication you should take away is "please be more specific and precise in your question text"):

The term "vector" is ambiguous. A vector could be a magnitude and direction or it could be a single dimensional array. Assuming the latter, then do you mean you are using a specialisation of the std::vector class template or something else? If you are using std::vector it would be nice to know vectors of what - std::vector<int> or maybe std::vector<std::string> or a collection of some other element type.

Anyhow having no information to the contrary I will assume you are using std::vector of int elements for both Rel and Wed:

   std::vector<int> Rel;
   std::vector<int> Wed;

The next point I am not clear on is whether you require Rel to only contain the contents of Wed or if the contents of Wed are to be appended to the end of Rel or some other merging of the two collections.

If you require the former behaviour, that Rel contains only the elements of Wed then another way of saying this is that Rel becomes a copy of Wed, so assignment of one vector to the other is appropriate:

   Rel = Wed;

However if what you wish is to be able to append Wed to the end of Rel then there are several ways to do it - the most obvious is to push_back each element of Wed into Rel. However there is another way to do this using the C++ standard library algorithms, specifically the copy algorithm (include <algorithm>):

   outputIterator  std::copy( inputIterator  sourceBegin
         , inputIterator  sourceEnd
         , outputIterator destinationBegin
         )

As can be seen the copy algorithm takes a source value sequence range to be copied and copies them to a destination sequence starting at destinationBegin and returns the position after the last copied element in the destination sequence (which may or may not be useful). As is common in the C++ standard library sequence ranges and positions are specified using iterator types which can be thought of as a generalisation of pointers or positions. A range is specified as a start position (e.g. sourceBegin) and an end position (e.g. sourceEnd) within a sequence. The end position always represents one past the end. For more on iterators see a good standard C++ library reference such as "The Standard C++ Library a Tutorial and Reference" by Nicolai M. Josuttis.

The obvious way to use std::copy to append the contents of Wed to Rel would be to use Wed.begin() and Wed.end() as the sourceBegin and sourceEnd arguments and Rel.end() as the desitinationBegin argument:

   std::copy( Wed.begin(), Wed.end(), Rel.end() );

However Rel.end() is one past the end of existing elements in the Rel vector and std::copy copies into existing elements in the destination sequence, thus the above line will not work as it is - it will most likely compile but not execute correctly.

Luckily help is at hand in the form of a special variety of iterator adapter types called inserters which create new elements as required rather than accessing existing elements. To append to a container we use a back_insert_iterator (include <iterator>) so called because elements are added to the back (or end) of the container using a container's push_back function. In fact we will use a convenience function back_inserter that creates a back_insert_iterator object for us when passed the container to back insert to. As iterator adapter types are iterators we can use such an adapter in place of Rel.end() for the destinationBegin argument to std::copy:

   std::copy( Wed.begin(), Wed.end(), std::back_inserter(Rel) );

Which does what we want.

Note that I said back_insert_iterator uses a container's push_back function to add new elements to a container. This implies of course that the container type has a push_back function, and all main standard sequence containers support this operation (vectors, lists, deques, strings). However the same cannot be said of the push_front operation. Only deques and lists provide this operation and so only these container types can use the front_insert_iterator, and its creation function front_inserter.

On the other hand vectors, lists, deques, strings can use the insert_iterator and its inserter creation function. This is a general insertion iterator that inserts elements starting at a specified position, and so requires an additional iterator parameter specifying the position to start inserting new elements. The following simple example inserts the elements of Wed beginning at the second element of Rel:

   std::vector<int>::iterator pos( Rel.begin() );
   ++pos; // 1st element plus one
   std::copy( Wed.begin(), Wed.end(), std::inserter(Rel, pos) );

Thus if Rel started out containing [0, 1, 2, 3] and Wed contains [99, 999, 9999] then after the above set of operations Rel would contain [0, 99, 999, 9999, 1, 2, 3].

There are quite a few algorithms in the C++ standard library, some of which may be of immediate interest such as copy_backwards and reverse_copy.

Again I urge you to read up on these matters in a good text such as the previously mentioned book (my copy is very well used!) as a quick Q & A forum such as this cannot do justice to such a broad set of topics (containers, iterators, algorithms etc. and how they fit together), nor is it the place to expect reference material to be reproduced in quantity. There are some online resources available. I have found some at:

http://www.sgi.com/tech/stl/ has reference material although some parts are non-standard.

http://www.cplusplus.com/ has information on various C++ topics including some on the Standard Template Library (STL) portion of the C++ standard library (that is the bit we have been discussing here).

http://www.digilife.be/quickreferences/PT.htm has a few papers on C++ topics including one on the STL. Note: sometimes I have had pop up windows appear from this site so make sure you are protected against such irritations.

Then there are more general resources including:

http://www.parashift.com/c++-faq-lite/ is a C++ FAQ which everyone should read to see if it answers their C++ query before asking others.

http://www.accu.org/ is the ACCU web site which has numerous book reviews (http://www.accu.org/index.php/book_reviews) and resource links (http://www.accu.org/index.php/articles/weblinks/c22/).

Hope this been of help.




---------- FOLLOW-UP ----------

QUESTION: Hello,

Thanks a Lot for helping me out. That was really helpful in getting on my code.
I have a question: always the elements get appended after the first element, for example Rel = [0, 1, 2, 3] and Wed = [99, 999, 9999] and after appending we get
Rel = [0, 99, 999, 9999, 1, 2, 3];
is it possible to get Rel as [99, 999, 9999, 0, 1, 2, 3];
I have tried using pos++ but was getting the same output as previous.



I have one more question regarding to vectors w.r.t reading text files.

I have 2 text file containing a set of integers. I am reading all the elements of those text files into 2 vectors of type vector<string>.
for example i have a text file "abc.txt" whose elements I will be reading into vector<string> abc. I have one more text file "xyz.txt" whose elements I will be reading into another vector<string> xyz.

Now, my goal is to use the elements of first vector as an index into the next vector and read that element.

for example, if the vector abc first element is 26, then I want to use this as an index into next vector to read the 26th element of the vector xyz and display it. I want to continue doing this till the end of the vector abc.

Can you help me out with this.
The code that I am using for reading the text files is as follows:

#include "stdafx.h"
#include <string>
#include <vector>
#include <fstream>
#include <iostream>
using namespace std;

void main()
{
 
 vector<string> m_vNeviList;
 vector<string> neviTrainName;
 vector<string> l_strTempName;
 string l_mline;
 string l_nline;
 neviTrainName.clear();
 m_vNeviList.clear();
 ifstream Nevi_infile ("C:\\Users\\\\Documents\\Visual Studio 2005\\Projects\\deleteNow\\bb3.txt ");
 while (getline(Nevi_infile, l_mline, '\n'))
 {
   m_vNeviList.push_back (l_mline);
 }

 cout << "Read " << m_vNeviList.size() << " lines.\n";
 for(int i =0; i < m_vNeviList.size(); i++)
    cout<<m_vNeviList[i]<<endl;
 ifstream neviTrain_infile ("C:\\Users\\Documents\\Visual Studio 2005\\Projects\\deleteNow\\aa1.txt ");
 while (getline(neviTrain_infile, l_nline, '\n'))
 {
   neviTrainName.push_back (l_nline);
 }

 cout << "Read " << neviTrainName.size() << " lines.\n";
 for(int i =0; i < neviTrainName.size(); i++)
    cout<<neviTrainName[i]<<endl;

/* for(int i = 0; i < neviTrainName.size(); i++)
     {
        l_strTempName.clear();
        string str = neviTrainName[i];
        string str1 = m_vNeviList[str];
        //cout<<str<<endl;
        //cout<<str1<<endl;
        //cout<<l_strTempName[i]<<endl;
        
 }*/
 
 
}

I think you have got my prolem. I need to use the elements of the text file that I read into vector as an index to reading the elements of another vector.

Thanks,
Rahul  

Answer
Yes. Do not use ++pos or pos++ - that is do not move forward from the beginning at all. Thus:

   std::vector<int>::iterator pos( Rel.begin() ); // 1st element
   std::copy( Wed.begin(), Wed.end(), std::inserter(Rel, pos) );

Inserting elements at the beginning (or other position except at the end) of a vector is a relatively expensive operation compared with appending to the end of the vector, which maybe I should have noted before. This is because all elements after the inserted element have to be moved down by copying them from one location to another. This is tedious for simple types like int but can be very inefficient for types that have high cost (in time) copy constructors.

However, depending on the type of the elements of the vector, for vectors with a small number of elements in which all the data fits into the cache of your processor it may still be quicker than using other potentially more suitable container types such as a list. This is due to the simple reason that operations done on data in the cache are so much quicker than having to go to main memory. You may of course need to pre-size the vector to prevent additional memory allocations and copies of existing elements to new memory areas when the vector grows beyond its current allocated space.

I have located some additional online STL related resources which you may be interested in reading:

http://www.mochima.com/tutorials/STL.html has what at a quick look seems to be a reasonable introductory tutorial on the STL.

http://www.pottsoft.com/home/stl/stl.htmlx is a STL tutorial course. It has some useful diagrams and tables – such as a table noting the relative times (constant vs. linear) for common operations on lists, deques (double ended queues) and vectors.

http://www.halpernwightsoftware.com/stdlib-scratch/quickref.html is an STL quick reference.

http://www.josuttis.com/libbook/ is the web site for the "The Standard C++ Library a Tutorial and Reference" book by Nicolai M. Josuttis I mentioned before. The examples from the book are on the site somewhere.

I will point out that I have only had time for a brief look at these sites so cannot vouch for the quality of the content – but they all looked like useful resources to me from what I did see.

Now to your problem reading vector element values from data in files. My first thought is that your main problem is that you are reading data from a file that is numeric (even though in text form of course) and storing it as strings then wishing to use it as numbers! It seems to me that it would be better to read them from the file into an integer and push this back into a vector of integer index values.

Assuming the data in your bb3.txt file looks something like so:

   26
   15
   37
   48
   4
   etc.

i.e. you have one number per line and these numbers represent indexes into the second vector then you can read them like so:

   if ( Nevi && Nevi.is_open() )
   {
       int index(0);
       while ( Nevi )
       {
         Nevi >> index;
         if ( Nevi )
         {
         m_vNeviList.push_back( index );
         }
       }
   }
   else
   {
       std::cerr << "Unable to open bb3.txt." << std::endl;
       return 1;
   }

The above code replaces your code:

   while (getline(Nevi_infile, l_mline, '\n'))
   {
       m_vNeviList.push_back (l_mline);
   }

It first checks that the bb3.txt file is open and outputs an error message and returns if the check fails. Note that you should always check that objects and values are valid and return values from function indicate success.

Next the code reads from the file stream repeatedly until an error or (hopefully) end of file occurs. Each time an index value is read using the stream extraction operator, operator>>. Remember the stream extraction and insertion (operator<<) operators convert to and from various types from and to text, including integer types, floating point types and Boolean types, plus user defined types if they have the operator functions defined for them. Hence the stream extraction operator is useful in reading integer index values from text files (for example) as it does the text to integer conversion for us!

The values are read into an int. Strictly I suppose it should really read into a variable of the same type used for indexes to the neviTrainName vector, which should be some kind of unsigned integer (see below for more details). However I have had problems with reading negative values into an unsigned integer type in that this condition does not trigger a read formatting error with all C++ libraries (notably the library supplied with Visual C++ 2005) so I find it best to read into a signed integer and add checks in my code (not shown here for brevity) to ensure the value read is valid. The read values are only pushed back to the m_vNeviList vector if the stream is not in error or at end of file.

To see the effect of entering a negative value when reading an unsigned integer try the following code:

   unsigned int ui(0);
   std::cout << "Enter an unsigned int value: ";
   std::cin >>  ui;

   if ( std::cin )
   {
       std::cout << "std::cin OK, ui = " << ui << "\n";
   }
   else
   {
       std::cout << "std::cin BAD\n";
   }

Try entering -1234 for example and see what results you get! With Visual C++ 2005 (from an x64 64-bit Debug build) I get:

   Enter an unsigned int value: -1234
   std::cin OK, ui = 4294966062

However with the GNU g++ 4.0.1 compiler on a 64-bit version of Linux I get:

   Enter an unsigned int value: -1234
   std::cin BAD

Of course m_vNeviList should now be a

   std::vector< integer_type_used_as_neviTrainName_element_index_type >.

In fact the index type to operator[] or at() for the neviTrainName vector (which I am leaving as a std::vector<std::string>) would be:

   std::vector<std::string>::size_type

So strictly speaking m_vNeviList would have to be:

   std::vector< std::vector<std::string>::size_type >.

However I suspect a std::vector<int> or std::vector<unsigned int> would do.

You can now access neviTrainName via m_vNeviList using expressions like these:

   neviTrainName[ m_vNeviList[3] ];
   neviTrainName[ m_vNeviList[i] ];
   neviTrainName.at( m_vNeviList.at(3) );
   neviTrainName.at( m_vNeviList.at(i) );

Both [] and the at() function allow access to elements of the vectors. The difference is that [] does no bounds checking on the index and at() does do bounds checking on the index and throws a std::out_of_range exception if the passed index value is less than zero or greater than or equal to the vector’s size. By the way remember that C and C++ index from 0 not 1 so the first element has an index of 0, the second an index of 1 and the last an index of size – 1.

You might have noticed that the type expressions above were getting rather long and involved, and of course if you change the element type of the neviTrainName vector and you defined m_vNeviList to hold this vector’s size_type then really you should change the type of m_vNeviList to suit. The problem is similar to using raw numbers (so called magic numbers) rather than named constants. The solution is similar – give the types a name, in fact an alias, a type alias:

   typedef std::vector<std::string>          TrainNameVector;
   typedef std::vector<TrainNameVector::size_type>   TrainNameIndexVector;

   TrainNameVector      neviTrainName;
   TrainNameIndexVector m_vNeviList;  

Oh, finally, in C++ we prefer to use ++i and similar in cases where either ++i or i++ will do. This is because user defined types can overload these operators and object++ is generally more expensive that ++object. This is because post increment requires a temporary copy of the previous value to be kept and needs to return this copy by value rather than by reference. In fact the canonical forms are:

   class T
   {

       ...

   public:

       ...

       T & operator++() // pre-increment
       {
       // do pre-increment operations on object

         return *this; // return reference to object
       }

       T   operator++(int) // post-increment
       {
         T tmp(*this); // take copy of object
         ++*this;      // do pre-increment on object
         return tmp;   // return copy by value
       }

       ...

   };

Again hope this is of help.  

C++

All Answers


Answers by Expert:


Ask Experts

Volunteer


Ralph McArdell

Expertise

I am a software developer with more than 15 years C++ experience and over 25 years experience developing a wide variety of applications for Windows NT/2000/XP, UNIX, Linux and other platforms. I can help with basic to advanced C++, C (although I do not write just-C much if at all these days so maybe ask in the C section about purely C matters), software development and many platform specific and system development problems.

Experience

My career started in the mid 1980s working as a batch process operator for the now defunct Inner London Education Authority, working on Prime mini computers. I then moved into the role of Programmer / Analyst, also on the Primes, then into technical support and finally into the micro computing section, using a variety of 16 and 8 bit machines. Following the demise of the ILEA I worked for a small company, now gone, called Hodos. I worked on a part task train simulator using C and the Intel DVI (Digital Video Interactive) - the hardware based predecessor to Indeo. Other projects included a CGI based train simulator (different goals to the first), and various other projects in C and Visual Basic (er, version 1 that is). When Hodos went into receivership I went freelance and finally managed to start working in C++. I initially had contracts working on train simulators (surprise) and multimedia - I worked on many of the Dorling Kindersley CD-ROM titles and wrote the screensaver games for the Wallace and Gromit Cracking Animator CD. My more recent contracts have been more traditionally IT based, working predominately in C++ on MS Windows NT, 2000. XP, Linux and UN*X. These projects have had wide ranging additional skill sets including system analysis and design, databases and SQL in various guises, C#, client server and remoting, cross porting applications between platforms and various client development processes. I have an interest in the development of the C++ core language and libraries and try to keep up with at least some of the papers on the ISO C++ Standard Committee site at http://www.open-std.org/jtc1/sc22/wg21/.

Education/Credentials

©2016 About.com. All rights reserved.