When I insert() an object into container such std::unordered_map, how can I get reference/iterator/pointer to its location without searching for it ( e.g. find(); that would mean unnecessary overhead ).
I mean, the container datastructure should know where it just stored my object, without searching.
consider this code:
class Node{
public:
int id;
double mass;
};
std::unordered_map<uint32_t,Node> nodes;
Node& tryInsertNode( uint32_t key, const Node& node ){
auto nod_it = nodes.find( key );
if ( nod_it == nodes.end() ){
nodes.insert( {key, node} );
nod_it = nodes.find( key ); // this is silly, I don't want to do this !!!
// nod_it = ??? // JUST GIVE ME MY POINTER !!!
}else{
nod_it->second = node;
};
return nod_it->second;
}
I need to return reference / pointer / iterator to the instance of class Node which is allocated inside std::unordered_map<uint32_t,Node> nodes; so that I can modify the contend of this node later, without paying cost of find()
Sure, I would not have this problem when I use pointers i.e. :
std::unordered_map<uint32_t,Node*> nodes;
But I think that in my particular case would be std::unordered_map<uint32_t,Node> preferable for performance reasons (i.e. less jumping in memory).
std::unordered_map::insert returns an iterator* to the newly-inserted element.
So you already have it. It's just that, in your code at the moment, you're throwing it away.
* Well, or a pair that wraps it. It depends on which insert you call. In your case:
nod_it = nodes.insert( {key, node} ).first;
Related
I have a C++ program that creates Huffman codes for all characters in file. It works good, but I want to create nodes without using new operator because I know that you shouldn't use it. I tried using a vector global variable for saving nodes but that doesn't work.
std::vector<Node> nodes;
Node* create_node(unsigned char value, unsigned long long counter, Node* left, Node* right) {
Node temp;
temp.m_value = value;
temp.m_counter = counter;
temp.m_left = left;
temp.m_right = right;
nodes.push_back(temp);
return &nodes[nodes.size() - 1];
}
Edit: I added more code, I did't really explained what doesn't work. Problem is in generate_code(), it never reaches nullptr. I also tried using Node and not Node* but the same thing happened.
void generate_code(Node* current, std::string code, std::map<unsigned char, std::string>& char_codes) {
if (current == nullptr) {
return;
}
if (!current->m_left && !current->m_right) {
char_codes[current->m_value] = code;
}
generate_code(current->m_left, code + "0", char_codes);
generate_code(current->m_right, code + "1", char_codes);
}
void huffman(std::ifstream& file) {
std::unordered_map<unsigned char, ull> char_frequency;
load_data(file, char_frequency);
std::priority_queue<Node*, std::vector<Node*>, Comparator> queue;
for (auto& node : char_frequency) {
queue.push(create_node(node.first, node.second, nullptr, nullptr));
}
while (queue.size() != 1) {
Node* left = queue.top();
queue.pop();
Node* right = queue.top();
queue.pop();
auto counter = left->m_counter + right->m_counter;
queue.push(create_node('\0', counter, left, right));
}
std::map<unsigned char, std::string> char_codes;
Node* root = queue.top();
generate_code(root, "", char_codes);
for (auto& i : char_codes) {
std::cout << +i.first << ": " << i.second << "\n";
}
}
The general answer is of course to use smart pointers, like std::shared_ptr<Node>.
That said, using regular pointers is not that bad, especially if you hide all pointers from the outside. I wouldn't agree with "you shouldn't use new", more like "you should realize that you have to make sure not to create a memory leak if you do".
In any case, for something like you do, especially with your vector, you don't need actual pointers at all. Simply store an index for your vector and replace every occurence of Node* by int, somewhat like:
class Node
{
public:
// constructors and accessors
private:
ValueType value;
int index_left;
int index_right;
}
I used a signed integer as index here in order to allow storing -1 for a non-existent reference, similar to a null pointer.
Note that this only works if nothing gets erased from the vector, at least not before everything is destroyed. If flexibility is the key, you need pointers of some sort.
Also note that you should not have a vector as a global variable. Instead, have a wrapping class, of which Node is an inner class, somewhat like this:
class Tree
{
public:
class Node
{
...
};
// some methods here
private:
vector<Node> nodes;
}
With such an approach, you can encapsulate your Node class better. Tree should most likely be a friend. Each Node would store a reference to the Tree it belongs to.
Another possibility would be to make the vector a static member for Node, but I would advise against that. If the vector is a static member of Node or a global object, in both cases, you have all trees you create being in one big container, which means you can't free your memory from one of them when you don't need it anymore.
While this would technically not be a memory leak, in practice, it could easily work as one.
On the other hand, if it is stored as a member of a Tree object, the memory is automatically freed as soon as that object is removed.
but I want to create nodes without using new operator because I know that you shouldn't use it.
The reason it is discouraged to use new directly is that the semantics of ownership (i.e. who is responsible for the corresponding delete) isn't clear.
The c++ standard library provides the Dynamic memory management utilities for this, the smart pointers in particular.
So I think your create function should look like follows:
std::unique_ptr<Node> create_node(unsigned char value, unsigned long long counter, Node* left, Node* right) {
std::unique_ptr<Node> temp = std::make_unique<Node>();
temp->m_value = value;
temp->m_counter = counter;
temp->m_left = left;
temp->m_right = right;
return temp;
}
This way it's clear that the caller takes ownership of the newly created Node instance.
Hello this is the code:
template <class T> class FibonacciHeap{
public:
class Entry{
public:
// Returns the element represented by this heap entry.
T getValue(){
return mElem;
}
// Sets the element associated with this heap entry.
void setValue(T value){
mElem = value;
}
// Returns the priority of this element.
double getPriority(){
return mPriority;
}
private:
int mDegree = 0; // Number of children
bool mIsMarked = false; // Whether the node is marked
Entry mNext; // Next element in the list
Entry mPrev; // Previous element in the list
Entry mChild; // Child node, if any
Entry mParent; // Parent node, if any
T mElem; // Element being stored here
double mPriority; // Its priority
//Constructs a new Entry that holds the given element with the indicated priority.
Entry(T elem, double priority){
mNext = mPrev = this;
mElem = elem;
mPriority = priority;
}
};
...
In the Class "Entry" I want to call Entry recursively, so I can use:
First_entry.mPrev.mNext
I know this works in Java, but when I compile this in c++, I get:
error: 'FibonacciHeap<T>::Entry::mNext' has incomplete type
Does anyone know how to fix this or work around this?
Based on the variable names and initializers here, I'm assuming you're adapting my Java Fibonacci heap into C++. :-) If so, best of luck!
In Java, if you have a variable of type Entry, it acts like a C++ variable of type Entry* in that it's a pointer to another Entry object rather than an honest-to-goodness Entry object. As a result, in the definition of the Entry class, you should adjust the fields so that they're of type Entry* rather than Entry. Similarly, instead of using the . operator to select fields, you'll want to use the -> operator. So
First_entry.mPrev.mNext
would be rewritten as
First_entry->mPrev->mNext
Don't forget to explicitly initialize the Entry pointers to nullptr - Java does this automatically, which is why there are no initializers in the Java version. However, C++ gives uninitialized pointers garbage values, so make sure to give mChild and mParent an explicit nullptr value.
I have a simple algorithm which returns a list of lists, where each inner list contains the nodes on a different level of a binary tree. I'm having trouble understanding how to "reset" the scope of my inner list (e.g. see below).
My tree is a simple toy tree like so:
struct Node {
int data;
Node *left, *right;
}
I use a simple bfs that should return a list of lists. I try to create a new list on each of the loops, but I'm not sure how to "clear" the list and start a new one.
std::vector< std::vector<Node *> > build_lists(Node *root) {
std::vector< std::vector<Node *> > result;
Node *newline = new Node { std::numeric_limits<int>::min(), nullptr, nullptr };
std::deque<int> q;
q.push_back(root);
q.push_back(newline);
Node *tmp;
std::vector<Node *> inner; // HERE IS WHERE IS SET THE FIRST INNER VECTOR
while(!q.empty()) {
tmp = q.front();
q.pop_front();
if (tmp == newline) {
result.push_back(inner);
std::vector<Node *> inner; // HERE IS WHERE I TRY TO ''RESET'' THE VECTOR
if (!q.empty())
q.push_back(newline);
} else {
inner.push_back(tmp);
if (tmp->left)
q.push_back(tmp->left);
if (tmp->right)
q.push_back(tmp->right);
}
}
}
Clearly, I have failed to understand scope and some basic language features. If anyone could help point me in the right direction, I would appreciate it.
You can't reset a variable by declaring it again, which is what your code is doing. You now have a second variable with the same name, that for the duration of the second variable that name points to the second variable.
Instead you need to use a method to clear the first variable.
vector does have a method to reset it's contents - vector::clear.
You should do this instead:
result.push_back(inner);
// std::vector<Node *> inner;
inner.clear();
If you need to clear something you've pushed inside a vector, you can do this:
vector< vector< int > > vvi;
vector< int > vi;
vi.push_back(1); // fill
vii.push_back(vi); // vii has a copy of vi as it is now.
auto & _v = vii[0]; // fetch a reference to the copy
_v.clear(); // clear it
vii[0].clear(); // same in one step
assert( vii[0].size() == 0 ); // still contains a vector at index 0 but it's empty
Notwithstanding the fact that you would be clearing vectors of pointers - as others have pointed out (ha) you need to be very careful not to 'lose track' of pointers.
Eg, this would be bad:
vector< int* > x;
x.push_back( new int(4) );
x.clear(); // leak
In order of an object to go out of scope, it has to be created INSIDE the loop, like this for example:
while(1) {
std::vector<int> v;
...
// at that point 'v' will go out of scope and thus destroyed
}
However, when you do this:
std::vector<int> v;
while(1) {
...
// 'v' is always the same at this point
}
You could use std::vector::clear() to reset your vector, but be careful, since you are using pointers. Unless you keeping track of the pointed objects, just clearing the vector would result to dangling pointers, thus Undefined Behavior and you don't want that to happen.
If you need to free the memory the pointers point to, then you should first delete the objects the pointers point to and then clear the vector.
How to overwrite operator() inside of class MyNode so that set::find can use, and a sets that stores MyNode*. Then I try to find pointer in set, whose data field is the same as in given object. The below code does not work as I expected. I set breakpoints in of operator method, but none stopped.
I understand I can define find struct compare{} outside of class MyNode, and then define sets like:
set sets
This is oK for me. Here I am wondering whether it is possible I can define compare inside of class MyNode.
My code is like:
class MyNode {
std::string data;
public:
MyNode();
MyNode(std::string str);
MyNode(const MyNode& orig);
virtual ~MyNode();
std::string getData();
bool operator<(const MyNode& node){
return data<node.data;
}
bool operator<( const MyNode* node){
return data<node->data;
}
};
void testset(){
MyNode* node1 = new MyNode("5S");
MyNode* node2 = new MyNode("AH");
MyNode* node3 = new MyNode("AH");
std::cout<<" "<<node2<<std::endl;
std::set<MyNode*> sets;
sets.insert(node1);
sets.insert(node2);
std::set<MyNode*>::iterator iter =sets.find(node3); // I expected node2 can be found, but it does not..
if(iter != sets.end()){
MyNode* no = *iter;
std::cout<<"find it "<<no<<std::endl;
}
}
Another question is if I only define set like:
set<MyNode> sets.
std::find(sets.begin(), sets.end(), findmethod("aa"))
Is this complexity O(N) or O(log N)?
As for the first question: std::set doesn't care about operator()(); it cares about operator<().
As for your second question: the std::find algorithm, unlike the std::set<T>::find method, is O(n).
_transaction is a private member variable of my class, declared as:
public:
typedef stdext::hash_map<wchar_t*, MyClass*, ltstr> transaction_hash_map;
private:
transaction_hash_map _transactions;
During cleanup I am trying to iterate through this list and free up any objects still unfreed. However I am getting an AV on the for line here:
for (transaction_hash_map::const_iterator it = _transactions.begin(); it != _transactions.end(); it++)
{
MyClass* item = (MyClass*)it->second;
if (item != NULL)
{
item->End();
delete item;
}
}
Re: What is ltstr?
private:
struct ltstr
{
enum
{
bucket_size = 8,
min_buckets = 16
};
bool operator()(wchar_t* s1, wchar_t* s2) const
{
return wcscmp( s1, s2 ) < 0;
}
size_t operator()(wchar_t *s1) const
{
size_t h = 0;
wchar_t *p = const_cast<wchar_t*>(s1);
wchar_t zero = L'\0';
while ( *p != zero ) h = 31 * h + (*p++);
return h;
}
};
The stack shows it inside the begin() method. Any ideas?
One possible thing I can think of is that your class has already been deleted elsewhere before you try to iterate through the hash_map, and thus begin() will be operating on garbage. Worth a check...
Also - how are your wchar_t*'s getting allocated/freed? The code you've shown doesn't appear to be dealing with those. I'm not sure how that would cause trouble in your loop, but it's worth thinking about.
One minor thing - you shouldn't need the (MyClass*) cast. The hash_map's values should be of that type anyway, so it's nicer to let the compiler enforce type checks than to possibly bypass them with an explicit cast. That shouldn't be making any difference here though.
As I understand you're checking your pointer against NULL for the "remaining" items that might have not been deleted yet. But for the items you delete before your cleanup stage, do you set the pointer to NULL?
Notice that when you delete an object the pointer is not automatically set to NULL. So if you're not doing that you're trying to delete the same object twice (because your if statement will always be true), what could cause an access violation.
The code below is an example that causes a double deletion. It can be fixed if you uncomment the line that sets the pointer to NULL.
#include <cstddef>
struct Item {};
int main()
{
Item * p = new Item();
delete p;
//If you don't make the pointer null...
//p = NULL;
if (p != NULL)
//You delete the object twice.
delete p;
}
EDIT: I see you're getting the error exactly on the for line. So I'm wondering...
Apparently you have a MyClass that contains a _transactions member, which is a hash table with MyClass pointers as the data type. If the clean up code is performed inside a member function of MyClass, is it possible that you're deleting (for some reason) the MyClass instance that owns the _transactions you're iterating?
In this case you could get an error at it++ statement inside the for since the this object no longer exists. (Naturally, the error could be somewhere else too, like on the delete itself.)
Make sure to call _transactions.clear() after the for loop.