Scope of an Object in a Loop - c++

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.

Related

How to avoid using new operator in C++?

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.

Weird behavior of recursive class in for loop

I have a class that contains a vector of pointers to objects of the same class.
class a {
public:
std::vector<a *> children;
};
With this variable declaration,
a *v = &a();
the following statements can be executed repeatedly without error.
(*v).children.push_back(&a()); //add a child to "v"
v = (*v).children[0]; //set "v" to the newly created child
If the above code is placed inside a for loop, it fails during the second iteration. Specifically, it fails to push back a new "a" pointer to children, so the children vector remains empty, and a "vector subscript out of range" error is returned. Below is a minimum working example.
#include <vector>
class a {
public:
std::vector<a *> children;
};
int main() {
a *v = &a();
(*v).children.push_back(&a());
v = (*v).children[0];
(*v).children.push_back(&a());
v = (*v).children[0];
for (int i = 0; i < 2; i++) {
(*v).children.push_back(&a());
v = (*v).children[0];
}
}
Irrespective of how many times the statements are repeated before the loop, the above program always fails during the second iteration of the loop. Is there something I am missing?
a *v = &a(); takes the address of a temporary value. NEVER DO THIS.
Instead, use new to allocate an a on the heap:
a *v = new a();
v->children.push_back(new a());
Note that you have lost any references to the original v as you keep reassigning it. This is a memory leak.

Get iterator or reference of object just inserted to container

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;

How to make pointer/reference on element in vector?

I have two vectors and for some elements (not all) i need them to be connected ~ if i delete/change this element in one vector it should be removed/changed in both.
Something similar like pointers:
int r = 10;
int *p= &r;
*p = 3;
For example in next code it should change myvector2[0].a to 7.
#include <iostream>
#include <vector>
using namespace std;
struct elt {
int a, b, c;
};
int main()
{
vector<elt> myvector;
vector <elt> myvector2;
elt elt1 = {1,3,3};
myvector.push_back(elt1);
elt *elt2 = &elt1;
myvector2.push_back(*elt2);
myvector[0].a=7;
cout << myvector[0].a << endl; //7
cout << myvector2[0].a << endl; //1
return 0;
}
How can i make this possible? Please help!!!
As tgmath explained, the issue is that you wind up with two different objects in your vectors, as the standard containers store elements by value. I would recommend that you use shared pointers to properly maintain the lifetime of the objects (note this uses C++11 features):
#include <iostream>
#include <vector>
#include <memory>
struct elt {
int a, b, c;
};
int main()
{
std::vector<std::shared_ptr<elt>> myvector;
std::vector<std::shared_ptr<elt>> myvector2;
auto elt1 = std::make_shared<elt>(elt {1,3,3});
myvector.push_back(elt1);
myvector2.push_back(elt1);
myvector[0]->a=7;
std::cout << myvector[0]->a << std::endl; //7
std::cout << myvector2[0]->a << std::endl; //7
return 0;
}
Pointers and references (and iterators) to elements of std::vector are invalidated whenever the vector reallocates, which can happen during insertion. So you can only keep these if the vector is guaranteed not to reallocate for the lifetime of the pointer/reference. This can be achieved if you don't insert into the vector, or if you call reserve() on it before you start (and before you acquire the pointer/reference/iterator), extending its capacity so that no reallocations will be necessary.
If you can't guarantee that, your only option is to keep the index instead of a pointer/reference. Of course, you will also need access to the vector itself for this to work, but you should be able to keep a pointer or reference to it, e.g.
typedef std::pair<std::vector<elt>*, size_t> ReferenceIntoVector;
myvector2.push_back(*elt2); adds a copy of *elt2 into myvector2
This means that both vector have their own copy of the elt object. And both of them are distinct from elt1.
If you change the one in the first vector, the second does not change at all.
For this purpose yiu would need a std::vector<elt*> to have different pointers to the same object in different vectors.
First, there is no built in way to automatically remove an element from one vector if you happen to delete it from another.
In my opinion there are two different challenges here:
How can I update an element pointed to by one list to be updated in another?
How can I remove an element from one list, and have that removed from the other?
The first question is already answered in the other posts: Use std::shared pointers instead of your native pointers. They are just as fast and will take care of all the memory management for you.
Please note: That this approach will only work as long as you are only changing the value of the element pointed to by the shared pointer, and not what the shared pointer is pointing to.
In other words/code:
std::vector<std::shared_ptr<elt>> vec1, vec2;
// Insert some elements
vec1.push_back( std::make_shared( elt{ 1, 2, 3} );
vec2.push_back( vec1.back() );
vec1[0]->x = 5; // OK, updated in both vectors.
vec1[0] = make_shared(elt {3,2,1}); // Error: vec1[0] is pointing to a new object.
The other challenge is much harder, how can you automatically delete an element from both vectors. The short answer is "You can't", the slightly longer is, you can but not directly.
Approach 1: Setting deleted elements to nullptr.
This approach is a bit dirty an not something I would recommend as it requires all code that use either vector to check whether an element is nullptr.
// Encapsulate the object inside a `std::unique_ptr`
std::vector< std::shared_ptr< std::unique_ptr< elt >>> vec1, vec2;
// Adding elements are done similarly as before
vec1.push_back( std::make_shared( std::make_unique( elt{ 1, 2, 3} )));
vec2.push_back( vec1.back() );
// Now to delete a element you would do as follows
vec1[0]->reset(nullptr); // Flag this element as deleted
vec1[0]->erase( vec1.begin() ); // Remove element from vec1
// Now let us assume we are iterating through the other vector at a later time:
for (auto it = vec2.begin(); it != vec2.end(); ++it ) { // Using iterators makes the code cleaner.
if ( **it == nullptr ) { // If the unique_ptr == nullptr
it = erase(it);
else {
etl & the_value = ***it; // Yes you need all three, one for the iterator, one for the shared_ptr and one for the unique_ptr...
}
}
As you can see this quickly gets hairy. But if that is all you need it can work.
Approach 2: (The better in my opinion) Is to use the Observer/Observed Design Pattern.
In order to implement this pattern you will need to exchange your vector for another class. Let us make a simple example:
// First, create an interface describing all operations you want to be
// informed about.
template<class T>
class SharedElementListObserver {
protected:
void elementDeleted( const shared_ptr<T> & elem ) = 0;
}
template<class T>
class SharedElementList : public SharedElementListObserver<T> {
std::vector<std::shared_ptr<T>> data;
std::unordered_set<SharedElementListObserver*> observers;
public:
void push_back( const T & elem ) { data.push_back( std::make_shared<T>( elem )); }
void push_back( std::shared_ptr &sptr ) { data.push_back( sptr ); }
shared_ptr<T> operator[] (int index) {
return data[index];
}
shared_ptr<const T> operator[] (int index ) const {
return std::static_pointer_cast<const T>( data[index] );
}
// This will cause all instances of elem in all lists
// linked either directly and indirectly to this
// to be removed.
void delete( int idx ) {
if (idx >= 0 && idx < data.size() ) {
shared_ptr<T> temp = data[idx];
data.erase( data.begin() + idx );
for (auto observer : observers) {
observer->elementDeleted(temp);
}
}
}
// Link another list to this one to listen to deletions.
void link( SharedElementListObserver* observer ) {
if (observer == this) return;
else if (observers.insert(observer).second) {
observer->observers.insert(this);
}
}
// Unlink previously linked observer.
void unlink(SharedElementListObserver* observer) {
observer->observers.erase(this);
this->observers.erase(observer);
}
protected:
void elementDeleted( shared_ptr<T> & elem ) {
for (int i = 0; i < data.size(); ) {
if (data[i] == elem)
delete(i); // Not infinite loop, because of test above.
else
i++;
}
}
// You also need to write an operator=, a copy-constructor and a destructor
// that ensures that there are no dead observers in the observers list.
};
Depending on your assumptions this class can be implemented in a number of different ways. Some probably simpler than what I just did.
Please let me know if there are any errors in the above code.

vector doesn't work in struct

I have struct like this:
struct element{
char ulica[10];
vector<int> dane[3];
int wolne;
int w;
element *lewy, *prawy, *ojciec;
};
And I'm implementing kind of AVL tree. When key's are the same i need to put some int values to dane (dane[0], dane[1], dane[2] describe 3 different value) so I use
tmp2->dane[0].push_back(number)
EDIT. Here is code where I'm adding a values to this vector, it's half of the function because secod half is about rotations in AVL.
void wstaw_wezel(){
element *tmp2; //tmp2 bedzie ojcem nowo wstawionego elementu
tmp2=korzen;
while(tmp2!=NULL){
if(strcmp(tmp2->ulica, tmp->ulica)<0){
if(tmp2->prawy!=NULL){
tmp2=tmp2->prawy;
}
else{
tmp->ojciec=tmp2;
tmp2->prawy=tmp;
cout<<"Wstawiam pod prawy "<<tmp2->ulica<<endl;
if(tmp2->w!=0) tmp2->w=0;
else tmp2->w=-1;
break;
}
}
else if(strcmp(tmp2->ulica, tmp->ulica)>0){
if(tmp2->lewy!=NULL){
tmp2=tmp2->lewy;
}
else{
tmp->ojciec=tmp2;
tmp2->lewy=tmp;
if(tmp2->w!=0) tmp2->w=0;
else tmp2->w=1;
cout<<"Wstawiam pod lewy "<<tmp2->ulica<<endl;
break;
}
}
else{
cout<<"2 bloki na tej samej ulicy"<<endl;
for(int i=0; i<tmp2->dane[0].size(); i++) cout<<tmp2->ulica<<" "<<tmp2->dane[0][i]<<endl;
tmp2->numery.push_back(tmp->numery[0]);
tmp2->dane[0].push_back(tmp->dane[0][0]);
for(int i=0; i<tmp2->dane[0].size(); i++) cout<<tmp2->ulica<<" "<<tmp2->dane[0][i]<<endl;
tmp2->dane[1].push_back(tmp->dane[1][0]);
tmp2->dane[2].push_back(tmp->dane[2][0]);
tmp2->wolne+=tmp->dane[2][0];
break;
}
}
if(tmp->ojciec==NULL){
korzen=tmp;
return;
}
where tmp2 is a pointer to this struct (I checked adrres where it points and every time it's the same adrres).
Where is problem? If I add new value to vector it is until the loop where i do it ends. Finally instead having fe. 4 values in vector i have one, the last added value. Vector don't add new value to the end, just replacing it.
You declare the initial size of a std::vector in its constructor, so one way you can accomplish this is:
struct element
{
char ulica[10];
std::vector<int> dane;
int wolne;
int w;
element *lewy, *prawy, *ojciec;
element() : dane(3) {}
};
If you don't include the constructor, the initial size of the vector will be 0. In any event, to add an element to the back, just use tmp2->dane.push_back(number); This will add the value in number to the back of the vector tmp2->dane which may result in a change in the amount of allocated memory for the vector instance.
UPDATE: Based on the comment from the OP that he needs three vectors try this:
struct element
{
char ulica[10];
std::vector<std::vector<int> > dane;
int wolne;
int w;
element *lewy, *prawy, *ojciec;
element() : dane(3) {}
};
To add elements to the vectors, simply use tmp2->dane[i].push_back(number) where i is the index of the vector to use, and number is the new number to add to the ith vector, which is the same convention you seem to be using in your code segment above.
Update 2: Based on additional information below, I think a redesign of your data structure is called for. You're mixing the meaning of the various components and by more clearly delineating the functions of the data element and the AVL data structure management, you will be able to more clearly distinguish between the two. So try this instead. Have a data structure specifically for the "value" portion of your tree nodes, as in:
struct house
{
int house_number;
int unique_value0;
int unique_value1;
house(int hn, int uv0, int uv2)
: house_number(hn),
unique_value0(uv0),
unique_value1(uv1) {}
};
template <typename VALUE> struct node
{
std::string key;
std::vector<VALUE> values;
int left, right;
node<VALUE> *leftNode, *rightNode, *parentNode;
};
From here, you create a root node:
node<house> *root;
When you want to add a house to a street, node<house> *s, all you need to do is
s->values.push_back(house(a, b, c));
Of course a better alternative is to use what C++ already has for this. That is, there is a structure in the standard library called std::multimap which does pretty much what you're trying to do. In this case, you can simple declare
std::multimap<std::string, house> myMap;
This probably won't use AVL balancing. It more likely will be a Red-Black Tree, but it's all done for you.
How do you alloc the struct element?, it seems that the vector<int> dane[3]; has been initialized successful, but its inner vector does not been initialised.
try to add a ctor method to the struct element?
struct element
{
char ulica[10];
vector<int> dane[3];
int wolne;
int w;
element *lewy, *prawy, *ojciec;
element()
{
dane[0] = vector<int>();
dane[1] = vector<int>();
dane[2] = vector<int>();
}
};