errors with a map of pointers to vectors of pointers - c++

This one is gonna sting a little bit :\ . This is my map declaration:
std::map<std::string, std::shared_ptr<std::vector<std::shared_ptr<BaseEntity> > > > m_EntityByClassList;
really, the only reason it's so long is because of the smart pointer declarations, it's pretty much just this:
std::map<std::string, std::vector<BaseEntity *> *> m_EntityByClassList;
still painful to read though. here's my problem. When i try to insert an object into a new vector in the map, i get an "access violation trying to read" runtime error.
the chunk of code that yields this error is this,
for(;;)
{
file >> classname;
if(file.eof())
break;
m_EntityList.push_back( m_factory.createInstance(classname));
m_EntityList.back()->Initialize();
m_EntityList.back()->GetParams(file);
m_EntityByNameList[m_EntityList.back()->GetName()] = m_EntityList.back();
m_EntityByClassList[classname]->push_back(m_EntityList.back());
}
now yes, there is certaintly alot of code i am omitting here, mainly because theres a good 35 or 36 objects and files that go into this blurb here so i'm just going to list off the things relevant. file is an std::ifstream to an open file, classname is an std::string, m_EntityList is a std::vector<std::shared_ptr<BaseEntity> > (shouldn't matter), m_EntityByNameList is a map with indices type std::string and objects type std::shared_ptr<BaseEntity> (also shouldn't matter) and then the map above. all the statements above the last one work exactly correct with verfied results. If the last line is omitted, it compiles and works just dandy.
So this question has nothing to do with the rest of the files, and is only about trying to add a vector to a map and add objects to it. If you feel you need anything else, ask and i'll see about getting it posted up here. Posting everything would easily be 4000-4500 lines.
Annnnyway, what i've learned about maps from this is that using the [ ] operator with an indice that has yet to exist will have one created for you along with a default constructed object at that index. So what i get from this is that an std::shared_ptr<std::vector<std::shared_ptr<BaseEntity> > > will be created at the index classname which means i should be able to access the vector whether it had been created beforehand or just before. So does anyone have any clue what's up?

Using operator[] on the map with a nonexistent key will indeed insert a new shared_ptr. However, what you aren't taking into account is that the new shared_ptr will be empty (i.e. a null pointer), and therefore dereferencing it will cause undefined behavior. A new vector will not be created - since you are using pointers to vectors, you'll need to allocate a new vector yourself.
If your map's value type was the vector itself rather than a pointer to it, it would be created automatically as you expect.

Related

How to correctly use map with smart pointers and custom classes as key and value

I'm trying to make map in which i will hold Teams as key and vector of Employees which are polymorphic as value. Some of the data will be loaded from file in future and the user will be able to add new teams and employees at any time.
This is the map that i came up with:
std::map<std::unique_ptr<Team>, std::unique_ptr<std::vector<std::unique_ptr<Employee>>>> teams;
And this is some test code where i tried to add new team and a member to it:
bool Company::addTeam(const std::string & projectName)
{
teams.emplace(std::unique_ptr<Team>(new Team(projectName)), std::unique_ptr<std::vector<std::unique_ptr<Employee>>>(new std::vector<std::unique_ptr<Employee>>()));
for (std::map<std::unique_ptr<Team>, std::unique_ptr<std::vector<std::unique_ptr<Employee>>>>::iterator it = teams.begin(); it != teams.end(); ++it) {
std::cout << it->first.get()->getProjectName();
it->second.get()->emplace_back(std::unique_ptr<Employee>(new Programmer("David", "Beckham", "9803268780", "Djoe Beckham", Employee::LevelThree, projectName)));
std::cout << "\n" << it->second.get()->at(0)->toString();
}
return false;
}
The code runs fine and im able to print the employee data but after closing the application it throws exception and visual studio open delete_scalar.cpp and triggers a breakpoint at this code :
void __CRTDECL operator delete(void* const block) noexcept
{
#ifdef _DEBUG
_free_dbg(block, _UNKNOWN_BLOCK); // break point
#else
free(block);
#endif
}
I'm trying to find out what i'm doing wrong and how to fix it. I have empty destructors for all Employees classes if that have something to do with the problem. If my idea looks very stupid and there is easier way to accomplish my goal please tell me. Thanks in advance.
Design-wise, one person can have multiple roles in different teams at the same time. It can be another class Role linking a person to a team. Neither team, no role conceptually own person objects though, so Role could use plain pointers to link Person with Team.
Your idea could work, if you would not have a problem within one of your class. Due to the missing parts, it's difficult to tell.
However, this is not the way to go !!
A map is meant to work with an index by value. A typical use case is to find back an item with an already existing key. Of course, you could use pointers as key, but the pointer would then act as a kind of id without any polymorphic operations.
On the other side, a unique_ptr is designed to ensure unique ownership. So only one unique copy of each pointer value exist. This makes it very difficult to use as map value:
auto team2 = make_unique<Team>();
teams [team2] = make_unique<vector<unique_ptr<Employee>>>(); // doesn't compile
The above code doesn't compile, because team2 is a unique_ptr and cannot be copied into the indexing parameter. Using it for searching or inserting an item, would require to move it:
teams [std::move(team2)] = make_unique<vector<unique_ptr<Employee>>>(); //compiles
assert (team2); // ouch
But once moved, the unique_ptr value is no longer in team2 which is now empty since the unique pointer is in the map key and it's unique. This means that you will never ever find back an added team.
Better alternatives ?
If you would want to really use a polymorphic pointer as a map key, you should at least use a shared_ptr, so that several copies can exist in your code. But I'd suggest that you use values only as a key
Now to the value part of the map. There is no benefit of making a unique_ptr of a vector: the vector itself is not polymorphic, and vectors are well designed for copying, moving and so on. Furthermore sizeof(vector<...>) is small even for very large vectors.
Use a vector<unique_ptr<Employee>> if you want the vector in the map to own the Employees, or vector<shared_ptr<Employee>> if you intend to share the content of the vector.

Referencing and inserting into a vector of list<nodes>

I am implementing a hashtable and am having trouble with the implementation. After literal hours of googling on this one thing, i've given up and was hoping to see of i could get any help here. The biggest issue is to do with the use of vectors in the HashTable(doesnt make sense to me, rather just use list<> but using it is required)
My main issue is to do with how to implement the insert function to add to the HashTable.
void HashTable::insert(ulint key,ulint value){ //insert data associated with key
HashNode nodeToAdd;
nodeToAdd.assign(key, value);
int index = hash_function(key);
this->table[index].push_back(nodeToAdd);
}
Now the issue im having is adding the HashNode to my HashTable.
for reference in HashTable, the field for the table is
typedef vector <list<HashNode> > Table;
Table *table;
So by my understanding
this->table[index].push_back(nodeToAdd);
is going to the vector HashTable[index], which at the index should be a list. and when it gets to that list, it should push_back the new node into the list.
However when compiled, i'm hit by an error(no matching function to call) and i don't understand why.
Your list stores objects of type HashNode, not type HashNode*.
So you need to decide which of those you want to use, and change the code accordingly.
If you want to keep storing HashNode, then your insert is wrong -- it should instead create the node on the stack and store it by value in the list.
If you want to store a pointer, then your Table type is wrong, and should instead be vector<list<HashNode*>> -- note it should be managed carefully since the pointers will not be automatically deleted.
Personally, I'd suggest you go with #1 and save yourself a whole lot of headaches. But if you insist on #2, then I suggest you stop using malloc and use new -- or better yet use std::unique_ptr or std::shared_ptr for automatic lifetime management.
Also noteworthy is your definition Table *table. This is baffling, since Table is a vector. Your insert function is dereferencing this pointer, expecting it to perhaps point to an array of Table values, when it's quite clear you actually think it's a vector. I'm pretty sure you don't want that to be a pointer.
Since I only just noticed that detail, I imagine that's the first source of your error, since table[index] is actually type Table, not type list<HashNode> and you were trying to call the non-existent function vector<list<HashNode>>::push_back(HashNode*).

shared_ptr in vector trouble - iterating and losing scope - getting corrupted data

I'm not new to C++ but I do mostly work in C# and other managed languages usually so I'm not that well versed in shared pointers etc.
I basically have a 3-dimensional map of shared_ptrs to objects of a custom class (for 3d purposes).
These shared_ptrs live inside the map and are referenced all over the project, so far so good. For a specific piece of functionality I store some of these shared_ptrs in a vector to be iterated over later in the code but this is where things seem to break down.
Say I have 100 of these objects with their pointers stored in the 3d map and 3 of them are added to the vector because they have special properties for functionality. When I'm traversing the vector I call functions on these 3 objects. This all works fine until the for loop (for the iteration) hits it's second run on element [1] of the vector, at this point element [0] is full of corrupted data (but element[1] needs to access element[0] to perform one of the functions) and this is where I get an error.
As I said, I'm not that well versed in shared_ptrs but I thought that the data wouldn't be corrupted because the actual object is created in the creation of the 3-dimensional map - also, the vector hasn't gone out of scope yet because I am only in the 2nd run through the for loop (with 3 objects and so 3 iterations).
I know I must be doing something wrong but I have no idea how to debug this further - basically when I step through the code and it comes to the 2nd run of the for loop, if I look at element [0]'s shared_ptr in the vector (using VS) it is all corrupted.
Here is the loop of the code so you can see. The vector is created in the constructor of the class this code is in and the map (where the shared_ptrs and objects are created) is in the main class of the application. Also, getAdjacent takes objA and objB as pointers and so "fills" objB with the data of the adjacent object to objA:
for(vector<shared_ptr<ObjectClass>>::iterator iterator = objects.begin(); iterator != objects.end(); iterator++)
{
shared_ptr<ObjectClass> objA = (shared_ptr<ObjectClass>) iterator->get();
shared_ptr<ObjectClass> objB;
m_3DMap->getAdjacent(objA, objB);
objA->move(objB);
}
Could it be something to do with the cast I perform on iterator->get()? I couldn't see any other way to do it because if I don't have that cast there VS says that it can't convert from ObjectClass* to shared_ptr which is confusiong to me too because I thought I have a vector of shared_ptrs?..
Thanks for your time and it would be great if anyone could help.
For starters, you should call get() on a shared_ptr very rarely. And using that pointer to init another shared_ptr you MUST NOT do, ever.
The raw pointer you construct the smart pointer from is passed for ownership. And ownership must be exclusive. The way you use it two shared_ptrs will own the same object resulting a premature, and later a double delete at some point, both putting you to UB land.
This is somewhat better than your original, but without seeing code chances are good that getAdjacent is also broken in some way.
for(auto i = objects.begin(); i != objects.end(); ++i)
{
const auto& objA = *i;
shared_ptr<ObjectClass> objB;
m_3DMap->getAdjacent(objA, objB);
objA->move(objB);
}
signature of getAdjacent should be something like
void getAdjacent(shared_ptr<ObjectClass> const& objA, shared_ptr<ObjectClass>& objB);
to have fighting chance.
Okay I found the problem I had.
It was in part to do with what Balog Pal said about putting the shared_ptrs in a vector even though they live in a map so I changed this so that the vector held pointers to the shared_ptrs. Although this may still be bad practice, it got around the issue - especially as the vector was local and lost scope after the function exited.
The original vector approach was implemented as I was getting a dereference on my map iterator somewhere in the function and so I decide to leave all map manipulation outside the iterating (hence storing the 'special' objects in the vector to be iterated over later).
I have now changed the implementation to get rid of this inefficiency as my manipulation functions now return a new map iterator and thus negating the problem.
It would have been nice if the error message was somewhat more helpful that it was because it took me some time to realize the iterator became invalidated because I had an insert() buried in one of the functions that was called on the 'special' objects.
Although this answer doesn't really answer the exact question I asked, it answers the problem as a whole and why I designed the function in the way I did in the question.
Thanks for the help Balog and itwasntpete for the answers and comments.

C++ Safe returning a pointer to an iterater after map insert?

So I have some C++ classes that use a map and key class for a sort of data structure. In my insert method I use the typical map.insert. I want this function to return a pointer so I can modify some values (not the one used for comparison) inside the element inserted. So I was wondering if this is safe to this..
template<typename T>
NodeT<T> * TreeT<T>::
MakeNode(PointT point)
{
NodeT<T> * prNode = new NodeT<T>;
//set the contents for the node
prNode->SetNode(point, m_dTolerance);
//Create the key class using the
VectorKey key(point, m_dTolerance);
//Store the key,node as a pair for easy access
return_val = m_tree.insert( pair<VectorKey, NodeT<T> >(key, *prNode) );
if (return_val.second == false)
//if return_val.second is false it wasnt inserted
prNode = NULL;
else
//it was inserted, get a pointer to node
prNode = &(return_val.first->second); //is this safe if I plan to use it later?
return prNode;
}
I seemed to learn the hard way that my original pointer (the one I created with new), was pointing to the wrong element after the insert. Can anyone tell me why that is? So I used the return_val iterator to get the right pointer. I kinda dont want to return an iterator but if its safer then I do...
Thanks!
You seems to have troubles in your code with pointers and values. First you allocate an object on a heap ( with new Node )
Then you use a copy of that object to sore within your map.
PS. And then you loose original object forever as do not free memory which leads to memory leak.
In your case - it is invalid because you return pointer to object which can be deleted at any time ( for example next time you add something to your map and map decides to reallocate it's tree, so it will copy objects to different places ).
Storing pointers as map values prevents this. The only thing you need to remember to clear them up when removing object from map and when removing map itself.
The easy way to handle that would be using smart pointers (boost::shared_ptr for example ) or smart map class (boost::ptr_map for example ).
To fix that - use pointers everywhere ( store a pointer as a map value ).
This way - you will be able to return pointer from this function and it will be valid.
So just turn your map to map*> and this should fix most of your problems.
Do not forger to delete objects when erasing them from the map.
This code sample is interesting because contains a several things wrongs or to avoid.
Implementation
The most important things are been said (mainly by Bogolt):
You are leaking memory, because allocate NodeT<T> from the heap and never free it again, since map will allocate a copy of the object, not the pointer. Indeed, you specify as parameter *prNode, not prNode.
You use the heap to allocate the object (will be copied into the map), but you assume you always allocate the object. Despite it will be the very most probably case, that is not alway true: new operator would be return null or throw a bad_alloc exception. The code does not handle it.
Anyway, you use the heap when is not really needed. (And you see the problems are you intriducing because that). You can just create the object in the stack and then insert into the map, avoiding the previous problems and typing less code.
Design
The function returns a pointer to the element in the map. Depending the program, is possible this is safe. But what happens if the code reference the pointer when the object is removed from the map? Better, if you are returning pointer, do not return a raw pointer. Use smart pointer (shared_ptr in this case) instead. Using shared_ptr you will have not problems with the object life.
Other reason to use smart pointers: because the insertion into the map imply a copy of the element, you are imposing a requirement to NodeT<T>: it has to be copy constructible. May be this requirement is not important for performance, but may be in other circumstances copying the object have drawbacks. If you use smart pointer (or boost::ptr_map), the object will be created just once and is not copied.
Style
Just some suggestion, but not too important:
instead type pair<VectorKey, NodeT<T> >(key, *prNode), type make_pair(key, *prNode). The code is more compact and clearer typing less.
Well I'd say that depends on is your map alive longer than anything that could use (and store) the pointer.
If yes, (ie, it's in some sort of singleton), you could go with it, but not very safe anyway since any code could delete the pointer.
The best option is to store boost::shared_ptr (or std:: since c++11) instead of raw pointers in your mapn and return only a weak_ptr after insertion.
That way, you're sure no other code can delete your pointer as long as the map holds it, and that no one can use a pointer that has been erased from the map.

enter values in structure using vectors

I have created this structure:
struct xlsmain {
vector<sub_parts> sb;
string name;
}
struct sub_parts {
vector<pio_parts> pio;
string name_pio;
string direction;
string partition;
}
struct pio_parts {
string pio_name;
vector<report_specs> report;
}
struct report_specs {
string name;
vector<string> value;
}
struct xlsmain* interface = new xlsmain[100];
The nesting is necessary because every element is related in a heirarchial manner. The problem I am facing right now is how to enter values in this structure.
EDIT: I don't like using push_back() because every time I'll have to declare a separate structure. So for instance if I want to add a sub part to xlsmain, I have to declare a variable:
sub_parts sb1;
Then I have to feed values into this structure until it is finished, when I can use:
interface[i].sb.push_back(sb1);
Further, if nesting is involved then a number of structures like sb1 will also have to be created. This leads to having to create a large number of variables just to enter even a single value in my structure.
An option would be to use a std::map instead of the vector, then you can do something like this
xmlsmain["some element"].sb["some other"].direction = "up";
In this way, elements "some element" and "some other" are automatically created.
Before the edit, your concern that push_back() was "too complex" wasn't clear. (It sounded like perhaps even you didn't like the name of the method...or something strange like that?)
I'll try to get to addressing the new issue raised. But I will reiterate that although you are using the standard library string and vector classes, your structures are not themselves getting the advantages of C++!
You don't have constructors, destructors, or methods. Those are the foundations of letting data objects "come alive" with "magic" behavior that lets clients of these classes write simpler, more abstract code. This is helpful even if your only "client" is just more of your own code!
Let's say before you had code like:
pio_parts pp;
pp.pio_name = "this string will be stored in the name";
pp.report.push_back(someReport);
pp.report.push_back(anotherReport);
If you add a constructor and a method to your structure, like this:
struct pio_parts {
string pio_name;
vector<report_specs> report;
pio_parts(string newName) {
pio_name = newName;
}
void addReport(report_specs newSpecs) {
report.push_back(newSpecs);
}
};
Then the code above gets nicer:
pio_parts pp ("this string will be stored in the name");
pp.addReport(someReport);
pp.addReport(anotherReport);
Though really, you've not done much than save yourself from having to know the name of the data member in pio_parts to add to. Now you remember a method name instead. You save a little typing, but push_back() was about as good.
HOWEVER if there were more related operations you need to do inside of addReport() than just add to the vector, you now have a place to put all that code. That way the user of your class can not worry about whatever bookkeeping is necessary to add a report...they just ask that it be done! Also, since there's no call to push_back() it's no longer necessary for whoever is calling addReport() to know that the list is being stored in a vector.
I've deliberately not even bothered to try and scratch deeper into the specifics of references, copy-construction, smart pointers, member initialization syntax, or even class vs struct. It's too deep a language. Take a time out ASAP and carefully read this short paper by Bjarne Stroustrup that lays out that clear contrast in methodology:
Learning Standard C++ as a New Language
Now I'll try to get on to your other concern. First of all, you do not have to create a named instance of a variable in C++ to pass it to a function. You can rewrite:
sub_parts sb1;
interface[i].sb.push_back(sb1);
...as instead:
interface[i].sb.push_back(sub_parts ());
Not particularly useful in this case, as the object is constructed empty...so you just pushed something useless. But if your constructor took parameters that filled the object, it would be fine. You can even build up arrays like this:
How can I initialize an array of objects whose constructor require two or more arguments?
But if your constructor takes a hardcoded list (as yours seem to), then there's been a bit of a fly in the ointment. While C++ can initialize ordinary arrays with values you code in directly, passing the ordinary array loses its length information. A vector would be better, but initializing them with hardcoded values is clunky:
What is the easiest way to initialize a std::vector with hardcoded elements?
You can see that people had pretty much the same complaint you did about having to write:
std::vector<int> ints;
ints.push_back(10);
ints.push_back(20);
ints.push_back(30);
That post lists some of the workarounds, but bleeding edge compilers (probably not what you're using) support:
std::vector<int> ints = {10, 20, 30};
Once you have those, it makes it extremely easy to do your "nested" style constructions.
As a final note: you seemed on an earlier comment to concretely ask about raw arrays vs vectors. For your interface you almost certainly want a vector. Here's a pitfall: using new xlsmain[100] on a raw array requires to remember to do a delete[] interface (and not just a regular delete interface):
delete vs delete[] operators in C++
Bear in mind there is also no realloc in C++ anyway. So if that's why you were dynamically allocating it, forget about that idea.
You can save yourself from trouble like this by just making interface a vector. You will be able to resize it if you ever need to, and also avoid hard-coding a so-called "Magic Number" into your program.