Vector overwriting object pointers - c++

Everytime I call this method, The information is overwritten. If I call this function with name = "greg" then it will cout greg, If I then input "carl" it will cout carlcarl. The constructor is empty, and group and _groups are declared in the header.
I've been stuck on this for about 6 hours and I'm at a loss. Can someone explain to me how to fix this? It's not shown, but "group" is an object pointer. I'm taking this as a college course and its pretty much self-taught, I have to follow strict instructions on what to use and what not to use. I can't use strings.
void GroupDB::addGroup(char* name)
{
group = new GroupInfo(name, _nextGid++);
_groups.push_back(group);
for(int i = 0; i < _size; i++)
{
cout << (*_groups.at(i)).getGroupName();
}
_size++;
}

It sound's like you're storing the name pointer itself, which points to a temporary array that's invalidated at some point after this function returns. Instead, you want to store a more persistent copy of the string data. Use std::string not char* to store strings. If you "can't use strings" as a condition of the exercise, then you'll need to allocate an array to store the string data in, just as std::string would.
In general, don't use pointers unless you really need to. I doubt you want to be messing around with new here, but should rather store GroupInfo objects directly in the vector (or whatever _groups is).

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.

vector of object all the same object after adding separate objects to it

So I'm adding a bunch of objects to a vector inside of my class. I am able to add everything, or it seems, just fine, but when I go to print the contents of the vector everything is the very last object that I added. Why is that? This is what it looks like when I add
this->packages.push_back(mc);
This is what it looks like when I call the object I want to print
std::cout << truck.packages[1]->getTrack() << endl;
I'm not sure what I'm doing wrong, vectors seem pretty straight forward on adding and removing items.
If I need to add anything else, please let me know.
std::cout << truck.packages[1]->getTrack() << endl;
std::cout << truck.packages[2]->getTrack() << endl;
Output: 11111
11111
This is how I create the object
if(type == 0){
Letter *letter = new Letter();
letter->setType("Letter");
letter->setWeight(weight);
letter->setTrack(track);
price = letter->cost();
letter->setPrice(price);
this->packages.push_back(letter);
truckWeight = this->getGross() + (weight / 16);
this->setGross(truckWeight);
this->numPackages++;
delete letter;
This is letter.cpp
Letter::Letter()
{
this->type = "Letter";
this->price = 0.0;
this->weight = 0;
this->trackingNumber = 0;
}
double Letter::cost()
{
int weight = this->getWeight();
if(weight > 32)
return 0.0;
else
return (0.5 * weight);
}
This is letter.h
class Letter: public Package{`enter code here`
public:
Letter();
double cost();
};
Made it with Letter. It doesn't matter which object I use. They all do the same thing
You have UB in your code. You have a vector of Letter*s. When you push_back these Letter*s, the vector just makes a copy of these pointers. Once you call delete on the original Letter*, these vector elements are now dangling. You can handle this in two ways :
Make package a vector<Letter> instead of vector<Letter*>. Now, you just happily push_back(or, preferable, emplace_back) Letters.
If you must use pointers, use unique_ptr(or some other smart pointer). So, package would be vector<unique_ptr>. When the vector gets destroyed, the unique_ptrs will take care of memory deallocation.
If you don't want to use a smart pointer, you will have to manage the deletes yourself. So, if package is a member of MyAwesomePackage(no pun intended), you would do this in the destructor:
MyAwesomePackage::~MyAwesomePackage()
{
///Other destruction
for(auto * letter : package)
delete letter;
}
While you don't show the declaration of the vector, your usage in this statement:
std::cout << truck.packages[1]->getTrack() << endl;
shows that you're not building a vector of objects, but a vector of pointers to objects.
If the vector contains only a pointer, and you modify the object referenced by this pointer after pushing the pointer into the vector, you will see the new value when you access it later through the pointer in the vector.
You are probably doing something like this:
std::vector<Package*> packages;
mc = new Package;
mc->setFoo(1);
packages.push_back(mc);
mc->setFoo(2);
packages.push_back(mc);
You now have two pointers to the same object in the vector. Since you modified the object after the first push_back() call, you will see the new value of the object when you access either element in the vector.
To make this work, you will need to either store actual objects in the vector:
std::vector<Package> packages;
or allocate a new Package object each time before you push the pointer into the vector.
After seeing the full code, your problem is a slight variation of this: You're deleting the object after pushing a pointer to it into the vector. So the vector contains pointers to objects that have been deleted, which leads to undefined behavior. What is probably happening in your test case is that when you allocate a new object after deleting the previous one, you're getting a pointer to the same memory again. That's why it looks like all values are the same. But even that is "random" and completely unspecified behavior.

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).

How to manage an array of pointers to objects?

I have a problem with an array of pointers to objects :(..
I need to generate a dynamic vector of object and then return it in
order to manipulate it in another class. In the code below there is
Event class that is abstract and CarArrival that inherits from it and
can be instantiated.
Inside the class that generate and fill the array I have this function:
Event** EventGenerator::getEvents() {
Event* cars[EVENTS];
for (int i=0; i<EVENTS; i++) {
cars[i] = new CarArrival(generator->getNextNumber(8,(float)sqrt(0.4)));
}
sort(cars, cars+(EVENTS), Event::cmp);
return cars;
}
I invoke this function in onther class in this way:
Event** cars = generator->getEvents();
for(int i=0; i<EVENTS; i++) {
cout << i <<":" << (*cars)[i]->getScheduleTime() << endl;
}
after the print of the first element i get "Segmentation Fault".
I have read some things online and I understand that I mistake since (*cars) evaluates to a
pointer to the first element of the array, in fact I can print the first element and not the other, but I cannot figure out how to access every element of the array in the second class.
How can I face this?
Thanks to all,
Alberto
I'd suggest that you use a std::vector<Event*> instead. You'll save a lot of pain this way. It takes care of all the nasty memory management in the background, and you can easily push any number of items into it. The best part in your case is, that you can simply return a vector which is not safe with a normal array.
Also your Event* cars[EVENTS]; is declared locally in you function. After you have finished it, it ceases to exist, which might cause your Segfault. You'd have to dynamically allocate the array with new, but still, try it with std::vector, see the documentation here.
EDIT: Sample Usage:
std::vector<Event*> EventGenerator::getEvents() {
std::vector<Event*> cars;
for (int i=0; i<EVENTS; i++) {
cars.push_back(new CarArrival(generator->getNextNumber(8,(float)sqrt(0.4))));
}
sort(cars.begin(), cars.end(), Event::cmp);
return cars;
}
std::vector<Event*> cars = generator->getEvents();
for(int i=0; i<cars.size(); i++) {
cout << i <<":" << (*cars)[i]->getScheduleTime() << endl;
}
I believe the cleanest way to handle a dynamic vector of pointers to dynamically allocated objects is to use a boost::ptr_vector. It handles everything you need, including allocation of the space to store the pointers, and deletion of those pointers afterwards.
Wouldn't be better to return vector<Event*> or vector<shared_ptr<Event>> instead of raw pointers? This way you would gain:
Automation of the memory management
Dynamic array with built-in length instead of fixed one
As mentionned by Constantinuis, you are returning the value of a pointer to a memory location that is only valid in the scope of the getEvents() function (it's allocated on the stack). You're bound to get a segfault next time.
You probably want to allocate the memory for this array in the heap (using 'new' if my C++'s isn't too rusty), and then you'll have to deal with freeing the memory later.
http://www.linuxtopia.org/online_books/programming_books/thinking_in_c++/Chapter13.html

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.