cannot assign to array of pointers in c++ - 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.

Related

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.

Vector overwriting object pointers

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

Pointers to pointers in hash Tables

i don't quite understand why the following code does what it does.
void initializeTable()
{
NodeT* (*hashTable) ;
*hashTable=(NodeT*)malloc(30*sizeof(NodeT));
int i;
for(i=0;i<30;i++)
{
(*hashTable)[i].info=(char*)malloc(10*sizeof(char));
strcpy((*hashTable)[i].info,"a");
cout<<(*hashTable)[i].info<<" ";
}
}
I am trying to understand how hash Tables work. In the first version of my program I used something like NodeT* hashTable[arraySize] in order to create an array of pointers to NodeT, in order to do the chaining. After this I decided to try and allocate dinamycally memory for the array, so I tried to use the (I think) equivalent NodeT* (*mockTable).
My question is the following: why do I have to use (*hashTable)[i].info, and not (*hashTable)[i]->info ? As far as i concerned, hashTable is a pointer which points to an array of pointers to NodeT, so this shouldn't happen. What am i getting wrong here?
You got the basic idea but it seem you miscount the pointers
your hashTable is a pointer to pointer (**), and when you access it in the strcpy you actually get into the value of this double pointer the 1'st is with the array index [i] and the second is with the *hashTable which is when accessing a variable calling the Value inside it.
so if you define a duel pointer variable
StrutT **temp;
and you want to access a field inside it
you can
*temp[i].field
but in case you would only want to access at level of a single pointer (same **temp) you would need:
temp[i]->field
or
*temp->field
which both only "pop" the top most pointer

How to access a vector through a pointer?

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

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.