Creating multiple class objects with the same name? c++ - c++

I'm making an application that is querying a MySQL database.
I want the results of this to be stored in a map (which has a corresponding pair):
std::map<int, Car*> m_car;
typedef std::pair<int, Car*> m_car_pair;
The car object is made up of 8 parameters, one of which is car_id so firstly I pull the car ID and use it as the key then I want to store the entire car object as the value of the map. (I know this is casing me to be storing the car_id twice but for the moment I don't mind that).
Anyway here's my query code:
void DatabaseController::getAll(QString query_string)
{
// Console log message
std::cout << "Querying Database" << std::endl;
/// Declare SQL query and pass function parameter 'query'
QSqlQuery query(query_string);
// execute the query
query.exec();
int count = 0;
// While results are coming in
while(query.next())
{
// Call car constructor passing all parameters
Car car(query.value(count).toInt(), query.value(count+1).toString(), query.value(count+2).toString(), query.value(count+3).toString(),
query.value(count+4).toInt(), query.value(count+5).toInt(), query.value(count+6).toInt(), query.value(count+7).toString());
if (car.getcarID() != 0)
{
m_car_records.insert(m_car_pair(car.getcarID(), &car));
}
}
std::cout << "Database query finished" << std::endl;
After this I made a quick test function to iterate over the map and pull all of the ID's (map key) and check they were different (i.e. the function worked) and they were.
But that was just a check what I needed was to be able to call the accessory functions from car on the car objects that should be in the map. So I used the same quick test function to iterate over the map and cout << car.toString(); (a simple to string function in the car class):
void DatabaseController::test()
{
m_car_records_iterator = m_car_records.begin();
for(unsigned int i = 0; i < m_car_records.size(); i++)
{
car *test = m_car_records_iterator->second;
std::cout << test->toString() << std::endl;
m_car_records_iterator++;
}
}
This showed the correct number of results however they all were the same i.e. the car object that has been added to every entry in the map is the same (the values of the last record that was found by the query)
My Question is...
Is there any way that using this structure I currently have for my query I can create and add these class objects to my map within the while loop using the same name for each, because of course I can't know how many results are being returned and declare a class object for each one, but as it stands using the same name is just adding the same one every time not actually replacing the values... at least that's what I think is happening??
Any advice or idea would be welcomed (sorry for the long post)

You are experiencing undefined behavior. The reason is that you insert a pointer to a local variable in the map.
In the loop in getAll, when the loop starts over on the next item the car variable is not valid any more.
I suggest you look into std::shared_ptr for the pointers.

This is your problem--
Car car( ... ); // ***Stack allocated
if (car.getcarID() != 0)
{
m_car_records.insert(m_car_pair(car.getcarID(), &car));
}
//But the pointer is what's stored
When the loop iterates, the Car instance is destroyed and the pointer dangles, resulting in undefined behavior. You need
Car* car = new Car( ... );
and then when m_car is no longer needed, you need to iterate through and delete its Car values.

Related

Delayed instantiation in C++

I'm trying to create a variable without instantiating the object.
In Python it would look like this:
graph = mymap[c] if c in mymap else Graph()
So I basically check if I already have that graph otherwise I create it. My understanding is that if I declare the following in C++ it will call the constructor and be wasteful.
Graph g;
So I'm trying to use pointers to avoid this "waste":
Graph* g;
if (graphs.find(c) == graphs.end()){
g = new Graph();
graphs[c] = *g;
} else {
g = &(graphs[c]);
}
std::cout << g << std::endl;
std::cout << &(graphs[c]) << std::endl;
The problem is that the addresses printed in the end do not match. In fact, my tests show that something weird is happening like a new instance of Graph is being created every time.
What am I doing wrong?
You actually don't have to do anything fancy here. You can simply do
Graph* g = &graphs[c];
The reason is that map::operator[] has the following behavior:
Returns a reference to the value that is mapped to a key equivalent to key, performing an insertion if such key does not already exist.
So if the key has an associated value already, you'll get a reference to that object. If that key does not yet exist, a value will be default constructed and inserted for you, and a reference to that new object will be returned.
Another answer already says the right way to do what you're asking, but I'll address (ha!) why the addresses don't match:
On this line:
graphs[c] = *g;
You're creating a COPY of the graph you just made with new that g points to, and that copy has a different address. You're then printing the address of the original and the address of the copy, so obviously they're at different addresses.
graphs[c] = ...
This code already does the allocation and construction of a (default) Graph object to be stored inside the map when there isn't one mapped to c already (which you already checked for).. the assignment after that just makes the Graph object it just created look like the one that you had previously created with the call to new.

Accessing an object member var using iterator

I have a vector of (pointers to) objects - people. I have a function that adds income to a person. The problem I'm having is to both find a person and accessing that addInc. I still sometimes get confused with pointers/references and more importantly I'm new to OOP. The relevant function is:
bool Population::Income(const string &id, unsigned int amount) {
Person *Candidate = new Person(id);
//find company using lower_bound
iterPeople = lower_bound(m_People.begin(), m_People.end(), Candidate, cmpId);
if ( iterPeople != m_People.end() && (*iterPeople)->m_id == id ) {
*(iterPeople)->addInc(amount);
//request for member 'addInv' in...maybe you meant to use '->'?
delete Candidate;
return true;
}
delete Candidate;
return false;
The rest of the code is HERE. I have two questions:
How do I solve the addInc issue?
About the lower_bound search - that method should be fine with NewPersonor CancelPerson but Income is gonna get called A LOT. Is that method sufficiently quick? Any way to make it more efficient?
BONUS - with addInc also comes MedianNetworth which returns median of all successfully added Incomes. The efficient way to use this is to create two heaps (min and max). My initial plan was to make_heap in the Population class:
make_heap(m_Audits.begin(), m_Audits.end(), cmpInt);
however I cannot make a heap inside the class because of unexpected '(' token - the very same syntax works in main() or inside any function. What am I doing wrong? Obviously I don't want to create heaps inside functions since I would have to create a new heap whenever I wanted to add an entry.

Updating an Object's property in a Vector

I have a vector which contains objects. The objects have a property called first name. I want to update the first name in a property, in order to do that i have to pass the vector which the objects are saved, staff number which uniquely identifies each object and finally the new name taken from the user input.
My problem is it displays the update name in the loop which i use to set the new name but if i use a second loop or a new loop and iterate through the vector again, the new name isn't saved but the old name is displayed.
Here is what i have done :-
public: void updateFirstName(vector<Administrator> vectorUpdate,string staffNumber,string newName)
{
FileHandler<Administrator> adminTObj;
for(Administrator iter: vectorUpdate)
{
if(iter.getStaffNumber()==staffNumber)
{
iter.setFirstName(newName);
cout<<"Update"<<endl;
cout<<iter.getFirstName()<<endl;
//here it prints the updated name but if i use a different loop
//and iterate through the vector the new name is not saved.
}
}
}
What seems to be the problem here ? Thank you
You pass a vector by value
void updateFirstName(vector<Administrator> vectorUpdate,
string staffNumber,string newName)
so each time you call this function you will copy original vector into it and work on this copied vector inside the function. The result of this is the changes are made to local variable inside function. Instead you want to pass vector by reference:
void updateFirstName( vector<Administrator> &vectorUpdate,
string staffNumber,string newName)
In function body, here
for( Administrator iter: vectorUpdate)
you will experienced the same thing. You want to write:
for( Administrator& iter: vectorUpdate)

Trouble with fields initialized via pointer

My problem involves the following function:
/*Adds the transaction to the head of the linked list attached to the account.*/
void Bank::Worker::UpdateTransactionHistory(int account_number, string transaction, Bank *our_bank_ptr) {
transaction_node new_trans;
new_trans.transaction = transaction;
if (our_bank_ptr->accounts[account_number].head == nullptr) { //If this is the first transaction
our_bank_ptr->accounts[account_number].head = &new_trans;
} else { //If this isn't the first transaction, disconnect the account from its current transaction list, connect the new transaction to the account and then connect the old list to the new transaction.
transaction_node temp;
temp = *(our_bank_ptr->accounts[account_number].head);
our_bank_ptr->accounts[account_number].head = &new_trans;
new_trans.next = &temp;
}
if (our_bank_ptr->accounts[account_number].head) //here the correct string is printed
cout << our_bank_ptr->accounts[account_number].head->transaction;
}
Its meant to update the transaction field of new_trans, which is then linked to the rest of the transaction list for a given account. Just before I return from the update transaction function I test to make sure the string was added in properly. The very last line of the function is cout << our_bank_ptr->accounts[account_number].head->transaction;,which outputs the transaction string correctly.
However, when I return from the function and immediately invoke the exact same line of code the compiler tells me that the transaction field the function updated still uninitialized. This is despite the fact that it was passed in as a pointer.
What the heck!? I thought that if I pass info into a function via pointers that anything I did to that info over the course of the function was permanent? What am I missing here?
Thank for your help,
Adam
You're setting the pointer to point to a local variable (new_trans). Once the function exits, this variable is destroyed and the pointer is dangling. So trying to dereference it results in Undefined Behaviour. In your case, this currently manifests as printing as unitialised. But it could do anything else.
If you need pointers there, and need them to point to persisting values, you'll have to allocate the values dynamically. But the real question is: do you need pointers?

How to get insertRows source?

I am instrumenting a model/view for item rearrangement and I am failing to understand how to override the insertRows method. Just for practice I am trying to wrap a std::vector with a custom structure.
std::vector<aStruct> mD;//my data
bool insertRows(int start, int rows, const QModelIndex & parent)
{
auto i = parent.row();
cout <<"I going to " << start << ":" << rows << " choosing "<< i<< endl;
beginInsertRows(parent, start, start + rows - 1);
aStruct blank(0);// Should be the value of the item being moved?
mD.insert(mD.begin()+start,blank);
endInsertRows();
return true;
}
Unfortunately, I can't seem to find a place to get at element that gives me a hold of the item being moved. How do I do this?
I assume the mD and insertRows are members of a custom model class.
insertRows doesn't receive any information about contents of inserted rows. Empty values should be inserted. Rows should be filled with data in the setData virtual method implementation.
You should use next steps to insert rows:
1. Call beginInsertRows
2. Modify your internal data structure
3. Call endInsertRows
There is all Ok in your sample.
View will insert empty rows (as #Riateche told) and will fill it automatically, after call of endInsertRows. All that you need is to override YourModel::data method to return correct data from you mD structure.
View will call YourModel::data method immediately after inserting empty rows. You don't need to do any extra operations. View will care about "filling" it.
Overriding of YourModel::setData method is mostly used for interaction between view and model, when user want to change data throught view widget.