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*).
Related
As I get it, C++17's node handles allow you to splice items inside and outside of some containers without actually moving the key and value objects. This is great, for example, if you need to keep some references to them in some other part of the program. In line of principle you can also keep a node handle outside of any container for some time, but still be able to access the key and value objects (and still without breaking references to it).
In my program it would make sense to directly create a node handle and later insert it in a map. Directly starting with the node handle (as opposed to create the key and value objects and inserting them in the map with the non-node handle insert call) would be great for me, because I might immediately take a reference to the value, which would remain valid even once the item is inserted in the map.
However, it doesn't appear that the is an appropriate constructor for std::map::node_type. In line of principle, I could just create an ad-hoc map, put a single item inside it and then splice it and destroy the map, but it seems a very un-C++-y way. Is there a better way? If not, is there a good reason not to do that?
The short answer to your question is - no, you have to use extract. Though there is problem with your idea how node handle works. You mistakenly think that reference to the value would remain valid after inserting the node into the new container - IT WILL NOT. Take look here:
Pointers and references to an element that are obtained while it is owned by a node handle are invalidated if the element is successfully inserted into a container.
I have the following class.
class Row
{
Table* parent_table;
Row(Table* construct);
}
class Table
{
std::vector<Row>;
}
I create an std::vector<Table> test;, and in a loop I create Table objects and push them back to test vector. The problem is I lose Table* inside each Row. I need the pointer so when I iterate through the my Rows, I know which table they belong to.
Is there a way to force std::vector, not to copy objects when they are pushed back?
Instead of storing Row in your std::vector, store std::unique_ptr<Row> or std::shared_ptr<Row>. These tend to be sufficient for this kind of thing (there are more complex ways you can approach this, such as storing raw pointers (complex to maintain) or rolling your own pImpl based class, or writing your own smart pointer with custom semantics).
unique_ptr only supports moving, and as such avoids your copy problem. shared_ptr instead supports shared ownership, which is probably something you don't need. I'd advise using unique_ptr.
Push elements by reference or pointer. You are pushing copies so copying the element is all C++ can do. I would personally advice you to use a vector of pointers like so: std::vector<Row*>
Actually a safer and cleaner way would be to store shared_ptr-s instead of bare pointers so that you don't have to deal with memory management that much. Something like:
std::vector<std::shared_ptr<Row> >
You are asking two unrelated questions. Please don't do that in one post. One post - one question. That makes answering really easier.
SECOND:
Is there a way to force std::vector, not to copy objects when they are pushed back?
Is already answered by others.
FIRST:
I create an std::vector test;, and in a loop I create Table objects and push them back to test vector. The problem is I lose Table* inside Row inside Table. I need the pointer so when I iterate through the my Rows, I know which table they belong to.
Most probably your ROW does not contain any backreference to the Table that contains it. That way, when you pass around the vector-of-rows or the row itself, you lose the information about who owns it. You must either pass a-vector-of-rows along with table* owner, or a row along with table* owner, OR you must make your Row more intelligent and force all Rows to contain a table* owner (that is, have the Rows contain a backreference to owner)
Of course the backreference may be of any shape. It does not need to be a pointer. You may pass Table&, a shared_ptr<Table>, a string tablename or whatever that will help you access the table either directly or through some manager. The important thing is, that either the Row must contain it, or it must be passed separately but along with the rows.
The easiest solution in this case would probably be to store an iterator to your table:
class Row
{
std:vector<Table>::iterator parent_table;
Row(std:vector<Table>::iterator construct);
}
I have a pointer p (not an iterator) to an item in a list. Can I then use p to delete (erase) the item from the list? Something like:
mylist.erase(p);
So far I have only been able to do this by iterating through the list until I reach an item at the location p, and then using the erase method, which seems very inefficient.
Nope, you'll have to use an iterator. I don't get why getting the pointer is easier than getting an iterator though...
A std::list is not associative so there's no way you can use a pointer as a key to simply delete a specific element directly.
The fact that you find yourself in this situation points rather to questionable design since you're correct that the only way to remove the item from the collection as it stands is by iterating over it completely (i.e. linear complexity)
The following may be worth considering:
If possible, you could change the list to a std::multiset (assuming there are duplicate items) which will make direct access more efficient.
If the design allows, change the item that you're pointing to to incorporate a 'deleted' flag (or use a template to provide this) allowing you to avoid deleting the object from the collection but quickly mark it as deleted. Drawback is that all your software will have to change to accommodate this convention.
If this is the only bit of linear searching and the collection is not big (<20 items say.) For the sake of expediency, just do the linear search as you've suggested but leave a big comment in the code indicating how you "completely get" how inefficient this is. You may find that this does not become a tangible issue in any case for a while, if ever.
I'm guessing that 3 is probably your best option. :)
This is not what I advice to do, but just to answer the question:
Read only if you are ready to go into forbidden world of undefined behavior and non-portability:
There is non-portable way to make an iterator from T* pointer to an element in a list<T>. You need to look into your std library list header file. For Gnu g++ it includes stl_list.h where std::list definition is. Most typically std::list<T> consists of nodes similar to this:
template <class T>
struct Node {
T item;
Node* prev;
Node* next;
};
Having pointer to Node<T>::item you can by using offsetof calculate this node pointer. Be aware that this Node template could be the private part of std::list so you must hack this - let say by defining identical struct template with different name. std::list<>::iterator is just wrapper over this node.
It cannot be done.
I have a similar problem in that I'm using epoll_wait and processing a list of events. The events structure only contains a union, of which the most obvious type to use is void * to indicate which data is relevant (including the file descriptor) that was found.
It seems really silly that std::list will not allow you to remove an element via a pointer since there is obviously a next and previous pointer.
I'm considering going back to using the Linux kernel LIST macros instead to get around this. The problem with too much abstraction is that you have to give up on interoperability and communication with lower level apis.
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.
I have a list
std::list<Selector> _selectorList;
I do parse on something and I get a smart pointer and a priority associated with the smart pointer. I implemented a structure to hold these two parameters like shown below.
struct Selector
{
int priority;
SmartPointer *selector;
}
There will be n number of parsing which will be done, as a result n number of struct instances will be pushed back into the list. At the end I am supposed to sort the list according to the decreasing order of priority variable in the structure. Currently, I plan to do this.
_selectorList.sort();
Is there any better approach than this, provided that I must use a list (only and nothing else) to store the smart pointers returned by parsing?
Like larsman told you using a pointer to a SmartPointer is highly probably wrong. Since smart pointers are used to avoid memory leaks, the reference counter is updated on object copy or assignment, so a SmartPointer * is probably useless.
For what concerne a better approach, you could reuse std::list::sort instead of reimplementing your own sort operation. The only things to do is let your Selector implement comparison operator in order to be able to sort your list.
Have a look here.