How to access a vector through a pointer? - c++

I want to insert new element into a vector using a pointer I have the following sample code:
struct info {
string Name;
int places; // i will use the binary value to identfy the visited places example 29 is 100101
// this means he visited three places (London,LA,Rome)
vector<int> times; // will represent the visiting time,e.g. 1,2,5 means london 1 time, LA
// twice and Rome five times
};
map<string,vector<info> *> log;
Peaple are coming from different cities, I will check if the city exists, just add the new person to the vector, else creat a new map object:
vector<info> tp;
info tmp;
if(log.size()==0|| log.count(city)==0) //empty or not exist
{
tp.push_back(tmp);
vector<info>* ss = new vector<info>;
ss=&(tp);
// create a new object
log.insert(map<string,vector<info> * >::value_type(city,ss)); // new object
}
else // city exist, just add the information to the vector
{
map<string,vector<info> *>::iterator t;
t=log.find(city);
*(t->second).push_back(tmp); //the problem in this line
}
How can I insert the new tmp into the vector?
The information to be read as follows:
Paris,Juli,5,3,6
Paris,John,24,2
Canberra,John,4,3
London,Mary,29,4,1,2

There are a lot of mistakes here, and they all stem from misusing pointers. The line that is mentioned as the cause of the problem is a minor syntactic issue. There are bigger issues at hand.
All of them can be easily solved by not misusing pointers. There is no reason to use pointers here, so the ultimate fix is to make the map have this type map<string,vector<info>> log;.
Then the code becomes something like this:
info tmp;
log[city].push_back(tmp);
// the [] operator creates a new empty vector if it doesn't exist yet
// there's no point in doing the checks by hand
Now that we have a simple solution, I'll mention the elephant in the room code.
vector<info>* ss = new vector<info>;
ss=&(tp);
// ...
log.insert(map<string,vector<info> * >::value_type(city,ss));
This sequence of operations will create a vector with dynamic storage duration, and immediately discard the sole pointer to it. That causes the vector that was just created to be lost, and the memory it uses is leaked; it cannot be recovered anymore.
To make matters worse, it sets ss to point to a local variable, and then saves that pointer to the local variable in the map. Because the local variable has automatic storage duration, it is gone once the function returns. That makes the pointer that was just stored in the map invalid, because it no longer has a vector to point to. After that, all kinds of havoc would be wreaked.

Looks like you need to do like this
(t->second)->push_back(tmp);

Related

cannot assign to array of pointers in c++

I need to keep an array of pointers in c++. I have a Set which keeps objects of my class. Then I need to iterate over the set and keep the pointer of each item in array. array is 2D by the way. so What I need in summary is :
pointers[1][4] = PinterToAnItem; the reason is I need to delete the item later, So I want to keep the pointer and directly go to that and delete it, instead of iterate again and find and then delete. But I have an error in assigning the array. I dont know how this idea will work, but so far it gives me error. Please let me know if it is not clear
the error is : " a value of type CONST Movie cannot be assigned to type of Movie"
struct Movie
{
int movieId;
int shopId;
int priceId;
};
Movie *pointers[100][100];
set<Movie> setMovie;
void main()
{
//reading and initialize the set with movies
// for example movie1 = {0,10,3} so I want to keep in my array a pointer to this object as:
// pointer [0][10] = ObjectPointer
// I have error in loop body. Also (*id), id and &id does not work.
for (auto id = setMovie.begin(); id != setMovie.end(); id++)
pointers[(*id).movieId][(*id).shopId] = &(*id);
}
2- by the way, do you guys think it is a fine Idea? can I delete pointer[0][10] which points to an object in my set? so by this way I don't need to iterate again through the set and delete (0,10). I have a pointer to (0,10) and I erase it from the set.
Thanks
EDITED
The answer from #Jeffry is right but it does not work for rest of my problem which is erasing the specified item. i intended to keep a pointer to an item and then erase it directly. But we know set.erase(ITERATOR) or (actual_value). so pointer here does not work for me. I had to change array of iterators, then it works. So I completed here maybe works for some later.
set<Movie> ::iterator pointers[100][100];
for (auto id = setMovie.begin(); id != setMovie.end(); id++)
pointers[(*id).movieId][(*id).shopId] = id; // I changed here as well
setMovie[1].erase(pointers[1][0]); // works well
// pointers[1][0] is an iterator to an item in set with movieid=1,shopid=0
pointers[(*id).movieId][(*id).shopId] = &(*id);
tries to store in pointers a pointer to a Movie, pointing into the setMovie.
Now consider what happens, after you do this. What if you did:
pointers[42][43]->priceId = 44;
That would possibly invalidate the set (you could have twice the same Movie). That is why the set doesn't let you do it.
One way around is:
const Movie *pointers[100][100];
Then, you could store the pointer because you wouldn't legally be allowed to modify movies, and mess up the ordering. I'm not sure that makes it a good idea, but it would solve your immediate problem.
For 2, no, it would not be a good idea to call delete on a pointer pointing to a movie stored in your set. You did not call new on this object, so you should not delete it. If you try, you'll get a crash immediately, or much later.

C++ Vectors insert new objects

I'm a self-taught c++ programmer (still at novice level).
I think I got an idea of how c++ works, but I can't wrap my head around this:
I want to create and populate an std::vector with different elements of a defined-by-me class:
// other code
while (getline(cfgDataStream, cfgData)) //parsing cycle of the config file
{
std::stringstream ss(cfgData); //creating a stream in order to fill fields
ss >> string1 >> IP1 >> IP2 >> PORT2 >> INDEX;
//they are all strings save the last one, which is a int
if (ss.fail())
{
//bad things happen
}
//FIRST IDEA: Using insert()
CModbusServer MBtemp* = new CModbusServer(this, IP2.c_str(), PORT2, INDEX)
std::vector<CModbusServer*>::iterator iterator = this->m_pServerCollection.begin(); //I get the vector initial position
m_pServerCollection.insert(iterator + (INDEX), MBTemp); // I put the new object in the right index (I don't trust the order in the config file)
//SECOND IDEA: Using push_back()
m_pServerCollection.push_back( new CModbusServer(this, IP2.c_str(), PORT2, INDEX)); //I attach each new object to the end of vector (i trust the order in the config file)
}
basically I want to create an object of CModbusServer and insert its pointer in a vector, so that I have n different CModbusServer objects in each vector position.
And this is where I get lost, i tried two ways of insertion (as shown in the code ) without success.
CModbusServer has, among others, a const char* ipAddress field. If I try to access to that field (i.e. to use it in a .Format(_T("%S)) function) I get random data. Trying to see why I noticed that in the vector I don't have n different objects,but n copies of the last object created with new CModbusServer(this, IP2.c_str(), PORT2, INDEX). Probably this happens due to the fact that I have a vector of pointers, but those should be pointers to different objects...
I'm using Visual Studio 2015 with MFC in order to realize a dialog-based application. I have an AppEngine class that calls method from other classes and has a vector of CModbusServer elements.
CModbusServer.h is as follows:
class CModbusServer
{
public:
CModbusServer(void *parentEngine, const char* , unsigned short , int );
~CModbusServer();
const char* ipAddress;
unsigned short port;
int indNode;
modbus_t *MBserver;
bool isConnected;
}
So, my question are:
1) Why can't I access to the ipAddress field (instead of reading "192.0.2.1" I read random characters) while I theoretically should be able to read it usingtheApp.CModbusServerVector[properIndex]->ipAddress?
2) I'm making a mistake in populating the vector, but I can't see where and, most importantly, why it's wrong.
Thank you for your assistance, and excuse my english and any omission.
EDIT:
The constructor code of CModbusServer is this:
CModbusServer::CModbusServer(void *pE, const char* ip, unsigned short nport, int ind)
: parentEngine(pE), //used in order to keep track of the parent dialog
ipAddress(ip),
port(nport),
indNode(ind)
{
this->isConnected = false;
this->m_socket = INVALID_SOCKET;
memset(&m_socketstructhint, 0, sizeof m_socketstructhint);
m_socketstructhint.ai_family = AF_UNSPEC;
m_socketstructhint.ai_socktype = SOCK_STREAM;
m_socketstructhint.ai_protocol = IPPROTO_TCP;
MBserver = modbus_new_tcp(ipAddress, (int)nport);
}
Please tell me if I omitted any other useful information.
Initially I used CString for managing the strings, but then I incurred in more and more issues and finally got a compiling and patially working code with const char*. I managed to enstablish a connection and to read the desired modbus registers, but then I got stuck on the isAddress printing problem.
modbus_new_tc(ip,port) is a method found in libmodbus library, a freeware library written for C that I had to use.
EDIT 2: related to angew answer:
So, If I'm right, what's happening is that I create a temporary set of pointers, that are used by the constructor (I've now added the relevant code). But shouldn't be the constructed object unrelated with what I've passed as argument? Aren't those argument copied? Sorry if the question is stupid, but I'm still learning.
the indices are sequential, though in the config file thay could as well be 0-1-2-3 (1 per line) or 0-3-1-2, that's what I meant by "don't trust them".
Since the push_back method has the same issues, probably the problem is back in the constructor. What puzzles me is that by doing a step by step execution I can see that with each iteration of the while loop I get new and correct data, but instad of being put in the i-th position, is put in the first i positions (i.e.: origin data: a b c, 1st run vector = a; 2nd run vector = b b,3rd run vector = c c c)
I didn't know std::unique_ptr<>, i'll look it up.
I've tried to use std:string or even CString, but the problem lies beneath libmodbus libraries.
Calling c_str on a std::string returns a "live" pointer to the internal data stored in that std::string instance. The pointer returned points to a buffer which is only valid as long as the std::string on which it was called remains alive and unmodified.
The constructor of CExtracalModbusServer just stores the pointer passed in. That pointer becomes dangling as soon as IP2 is re-assigned in the next iteration of the input loop. (The address pointed to is still the same, but the buffer which was previously located at that address has either been overwritten, or freed, or something else. In other words, the pointer is just dangling).
As for inserting into the vector: the first way (with insert) could only work if the indices in the files are sequential and starting at 0. You need a valid iterator into the vector to insert into it, where valid here means either pointing to one of the elements already in the vector, or the past-the-end iterator (the one returned by end()). If INDEX is equal to or larger than the size of the vector, then m_pServerCollection.insert(iterator + (INDEX), MBTemp); will attempt to insert outside the vector (essentially a buffer overfow). Ubnefined behaviour ensues.
The push_back way of inserting data should work, and if you see it misbehaving, it's either an artifact of the earlier bug (the one with dangling pointers), or there is a separate problem in code which you haven't shown.
Unrelated to the question at hand, but the code contains pretty bad practice in the form of manually managed dynamic memory. Instead of storing a CModbusServer* in the vector and managing the memory manually with delete in all the right places, you should be using std::unique_ptr<CModbusServer> which will take care of proper deallocation for you, even in the presence of exceptions.
If CModbusServer is under your control, you should likewise change it to store a std::string instead of a const char*. Never use C-style strings in C++ unless you have to interact with C-style APIs, and even in such case, limit their use only to the interaction itself. It's the same principle all over again: don't manage memory manually.

Why is the dynamically allocated array attribute of my class template only able to store one item?

I am trying to expand the functionality of a class template I created. Previously it allowed you to use key-value pairs of any type but only if you knew the size of the arrays at compile time. It looked like this:
template <typename K, typename V, int N>
class KVList {
size_t arraySize;
size_t numberOfElements;
K keys[N];
V values[N];
public:
KVList() : arraySize(N), numberOfElements(0) { }
// More member functions
}
I wanted to be able to use this for a dynamic number of elements decided at run-time, so I changed the code to this:
template <typename K, typename V>
class KVList {
size_t arraySize;
size_t numberOfElements;
K* keys;
V* values;
public:
KVList(size_t size) : numberOfElements(0) {
arraySize = size;
keys = new K[size];
values = new V[size];
}
~KVList() {
delete[] keys;
keys = nullptr;
delete[] values;
values = nullptr;
}
// More member functions
}
The new constructor has one parameter which is the size that will be used for the KVList. It still starts the numberOfElements at 0 because both of these uses would start the KVList empty, but it does set arraySize to the value of the size parameter. Then it dynamically allocated memory for the arrays of keys and values. An added destructor deallocates the memory for these arrays and then sets them to nullptr.
This compiles and runs, but it only stores the first key and first value I try to add to it. There is a member function in both that adds a key-value pair to the arrays. I tested this with the Visual Studio 2015 debugger and noticed it storing the first key-value pair fine, and then it attempts to store the next key-value pair in the next index, but the data goes no where. And the debugger only shows one slot in each array. When I attempt to cout the data I thought I stored at that second index, I get a very small number (float data type was trying to be stored), not the data I was trying to store.
I understand it might be worth using the vectors to accomplish this. However, this is an expansion on an assignment I completed in my C++ class in school and my goal with doing this was to try to get it done, and understand what might cause issues doing it this way, since this is the obvious way to me with the knowledge I have so far.
EDIT: Code used to add a key-value pair:
// Adds a new element to the list if room exists and returns a reference to the current object, does nothing if no room exists
KVList& add(const K& key, const V& value) {
if (numberOfElements < arraySize) {
keys[numberOfElements] = key;
values[numberOfElements] = value;
numberOfElements++;
}
return *this;
}
EDIT: Code that calls add():
// Temp strings for parts of a grade record
string studentNumber, grade;
// Get each part of the grade record
getline(fin, studentNumber, subGradeDelim); // subGradeDelim is a char whose value is ' '
getline(fin, grade, gradeDelim); // gradeDelim is a char whose value is '\n'
// Attempt to parse and store the data from the temp strings
try {
data.add(stoi(studentNumber), stof(grade)); // data is a KVList<size_t, float> attribute
}
catch (...) {
// Temporary safeguard, will implement throwing later
data.add(0u, -1);
}
Code used to test displaying the info:
void Grades::displayGrades(ostream& os) const {
// Just doing first two as test
os << data.value(0) << std::endl;
os << data.value(1);
}
Code in main cpp file used for testing:
Grades grades("w6.dat");
grades.displayGrades(cout);
Contents of w6.dat:
1022342 67.4
1024567 73.5
2031456 79.3
6032144 53.5
1053250 92.1
3026721 86.5
7420134 62.3
9762314 58.7
6521045 34.6
Output:
67.4
-1.9984e+18
The problem (or at least one of them) is with this line from your pastebin:
data = KVList<size_t, float>(records);
This seemingly innocent line is doing a lot. Because data already exists, being default constructed the instance that you entered the body of the Grades constructor, this will do three things:
It will construct a KVList on the right hand side, using its constructor.
It will call the copy assignment operator and assign what we constructed in step 1 to data.
The object on the right hand side gets destructed.
You may be thinking: what copy assignment operator, I never wrote one. Well, the compiler generates it for you automatically. Actually, in C++11, generating a copy assignment operator automatically with an explicit destructor (as you have) is deprecated; but it's still there.
The problem is that the compiler generated copy assignment operator does not work well for you. All your member variables are trivial types: integers and pointers. So they just copied over. This means that after step 2, the class has just been copied over in the most obvious way. That, in turn, means that for a brief instance, there is an object on the left and right, that both have pointers pointing to the same place in memory. When step 3 fires, the right hand object actually goes ahead and deletes the memory. So data is left with pointers pointing to random junk memory. Writing to this random memory is undefined behavior, so your program may do (not necessarily deterministic) strange things.
There are (to be honest) many issues with how your explicit resource managing class is written, too many to be covered here. I think that in Accelerated C+, a really excellent book, it will walk you through these issues, and there is an entire chapter covering every single detail of how to properly write such a class.

accessing a vector of objects that has a vector of objects that has a vector of objects from another function

I have an object in my main that is a Schedule object called sched. This object contains a vector of Flight objects called flights and each flight object has a vector of Seat objects called seats. There's more stuff going on, but this is the part that I'm having trouble with right now. What I want to do is access and change some of the variables inside these vectors (adding a passenger name to a seat on a specific flight and changing the occupied status of that seat, etc). I think I have figured out that I need to pass a reference to the flights vector to the function that is gathering the information about the passenger and I'm doing that by placing this function in the Schedule class:
vector<Flight>& Schedule::getFlightPtr()
{
return flights;
}
Now how do I use that reference to that vector in the calling function? This is what I have so far.
string passName;
string fltNum; //yes, the fltNum should be a string
vector<Flight>* flts;
//get the passenger name for the boarding pass and the flight number
cout << "Enter the Passenger's name: ";
cin.ignore();
getline(cin,passName);
cout << "Enter the Flight#: ";
cin >> fltNum;
//get a pointer to the vector of flights in the schedule
*flts = sched.getFlightPtr();
//locate the proper flight and add information to the seat
for (int idx = 0; idx < flts->size(); idx++)
{
if (*flts[idx]->getFlightNumber() == fltNum) //<----error states that flts must
{ //have a pointer type on these 3 lines
*flts[idx]->setSeatName(passName); //<----
*flts[idx]->setSeatOccupied() = true; //<----
}
}
Thanks for any insight you can give me on this problem.
Ok, so now it's compiling and seems to be working, however I cannot get anything to save in my vectors. Remember, I have nested vectors within vectors. Do I need to have all of the vectors be passed by reference?
*flts = sched.getFlightPtr(); is illegal as you are trying to derefence a null pointer, what you need to do it std::vector<Flight>& flights = sched.getFlightPtr(); and then use flights as a regular vector object.
You could also remove the asterisk from your flts, and change sched.getFlightPtr() to &sched.getFlightPtr() (or have getFlightPtr() return a pointer).
The problem is:
vector<Flight>* flts;
*flts = sched.getFlightPtr();
The first line creates a pointer that does not point anywhere yet. But then you dereference it, causing undefined behaviour. In practice , what might happen is for the program to treat a random chunk of memory as if it had a vector<Flight> in it.
Instead, you need to create a vector<Flight> before you actually try and use it. The simplest way is to replace the first line with:
vector<Flight> flts;
and remove all your dereferencing operations on flts.
You didn't show enough code for me to be sure what is going on in your program. If your intent is to refer to the flights that is presumably a member variable of the Schedule class (as opposed to taking a copy of that), then you could do:
vector<Flight> &flts = sched.getFlightPtr();
Then flts directly refers to the member of Schedule. (BTW this is bad design as it breaks encapsulation).

In C++, how do I push an object to a vector while maintaining a pointer to the object?

In my code, I have a vector of Student objects.
vector<Student> m_students;
I want to:
Check to see if the vector contains any Student of a certain name.
If no such Student exists, add a new one.
Add data to the Student of that name.
Consider the following code:
// Check to see if the Student already exists.
Student* targetStudent = NULL;
for each (Student student in m_students)
{
if (student.Name() == strName)
{
targetStudent = &student;
break;
}
}
// If the Student didn't exist, add it.
if (targetStudent == NULL)
{
targetStudent = new Student(strName);
m_students.push_back(*targetStudent);
}
// Add the course info to the Student.
targetStudent->Add(strQuarter, strCourse, strCredits, strGrade);
When I make the call to m_students.push_back(*targetStudent); it seems that the vector "m_students" ends up with a copy of the Student object that "targetStudent" points to at that time.
The subsequent attempt to add to targetStudent does not change the object contained in the vector.
How can I, starting with a pointer to an object, add that object to a vector and then access the object that is in the vector?
Get the pointer to the object after it was inserted into the vector, so instead of
targetStudent = new Student(strName);
m_students.push_back(*targetStudent);
use
m_students.push_back(Student(strName));
targetStudent = &m_students.back();
Also note that your example leaks memory, the targetStudent copied into the vector is never deleted.
Furthermore keep in mind that the pointers into the vector become invalid when new elements are added (if the vector increases in phsyical size and elements have to be copied into the new, larger vector all pointers into the previous buffer become invalid).
STL containers copy the objects they contain. There is no way to work around this.
You can, however, have a std::vector<std::shared_ptr<Student> >, which allow you to have a container of smart pointers. For this to work, though, your objects must all be attached to the shared_ptr at the time of construction.
So, something like:
std::vector<std::shared_ptr<Student> > m_students;
std::shared_ptr<Student> targetStudent;
for each (std::shared_ptr<Student> student in m_students)
{
if (student->Name() == strName)
{
targetStudent = student;
break;
}
}
// If the Student didn't exist, add it.
if (!targetStudent)
{
// creates a new Student and attaches it to smart pointer
targetStudent.reset(new Student(strName));
m_students.push_back(targetStudent);
}
std::shared_ptr is defined in the <memory> header in C++11. (In TR1, you can use std::tr1::shared_ptr instead.) If you're using C++98 without TR1, or need to be portable with it, you can use boost::shared_ptr instead; download from Boost.
You've already gotten a reasonably direct answer to your question. Based on what you seem to be trying to accomplish, however, it seems to me that a less direct answer may really be a better one.
At least as I read your description, you have a number of unique students, and a number of courses for each. When a student has completed a course, you want to look for the student. If they're not in the collection, add them. Then add the data for the course they completed.
That being the case, a vector strikes me as a less than ideal solution. You could implement code a couple of different ways, but I'd probably do it like this:
struct course {
std::string Quarter_, Course_, Credits_, Grade_;
using std::string;
course(string const &q, string const &c, string const &cr, string const &g)
: Quarter_(q), Course_(c), Credits_(cr), Grade_(g)
{}
};
std::map<std::string, std::vector<course> > m_students;
Using this, your entire sequence to look up a student, insert a new student if there isn't one by that name, then adding the course work to the (new or existing) student's record would work out as:
m_students[strName].push_back(course(strQuarter, strCourse, strCredits, strGrade));
Getting back to your original question, the standard containers are intended to work with values. You pass a value to them, and they store a copy of that value. One consequence of that is that anything like push_back(new XXX) is essentially always a mistake (pretty much a guaranteed memory leak). If you have an object, just pass it. If you don't, just create a temporary and pass that. In Java (for one example) seeing new XXX all over the place is routine and nearly unavoidable. While you can write C++ that way as well, it's not something you should expect as a rule.