views:

63

answers:

3

The following doesn't work as desired (print 2) because, I guess, the nodes are passed by value even though the vector is passed by reference. How could I fix it?

#include <iostream>
using std::cout;
using std::endl;

#include <vector>
using std::vector;

class Node{
    public:
        int value;
        Node(int);
        void createChildren(vector<Node> &);

};

//! constructor of a single node
Node::Node(int value)
{
    this->value = value;
}

void Node::createChildren(vector<Node> &nodes)
{
    for (int i = 0; i < 5; i++) {
        Node n(0);
        nodes.push_back(n);
        if (i == 0) {
            value = nodes.size();
        }
    }
}

int main(void) {
    Node a(0);
    vector<Node> asdf;
    asdf.push_back(a);
    asdf[0].createChildren(asdf);
    cout << asdf[0].value << endl;

    return 0;
}
A: 

If you are going to put the Node class into a vector (or any other container for that matter) you need to ensure that it has a copy constructor and and operator= implementation, otherwise you should put pointers to Node in the vector

class Node {
   ...
   public:
      Node(const Node& rhs) { ... }

      Node& operator=(const Node& rhs) {
           if(this == &rhs)
              return *this;
           value = rhs.value;
           // ... other members copied ...
           return *this;
      }

      Node& operator=(Node& rhs) { ... non const version ... }
 };

Outside of this your createChildren() method should set the value to 5 after the loop.

Adrian Regan
The default copy constructor and assignment operator preserve the value.
Magnus Hoff
Actually, that's right. Only really need it for deeper copying, it's good practice though.
Adrian Regan
But it doesn't help for anything in this case, does it?
Magnus Hoff
You are right. I have edited the answer. I really think that pointers should be used with vector,list,map as actual pointers or smart pointers. By value, is a dangerous habit to get yourself into.
Adrian Regan
+2  A: 

When you execute the line nodes.push_back(n);, the vector is resized, invalidating previously held references as it copies the existing members to a newly allocated memory block. In your case, *this inside createChildren is such a reference (to asdf[0]). Changing value in it is no longer defined behavior because the destructor for this object has been executed (try defining ~Node() and see when it is called)

Cubbi
So, I should modify the object which calls createChildren before modifying the container, right?I tried `value = nodes.size() + 1` at the beggining of the method and it works.
myle
@myle yes, or you could have `CreateChildren(std::vector<Node>`.
Cubbi
+1  A: 

The solution is somewhat related to what Adrian Regan sais.

If you push another element onto the vector-of-nodes in the "createChildren()" method, there's a fair chance that the vector needs to resize itself. When it does that it copies any existing elements over to the newly allocated storage.

So, the first time this happens it is copying the inital value of node 0 with value 0.

The compiler will generate a default copy constructor which does a bitwise copy. However, just implementing a copy constructor is not going to help since you will always lose the updated value of node 0.

h.

haavee