Try running the program again. You should get an error message that says something like cannot access private member declared in class strom::Node
. This is because we are trying to change data members (such as _left_child
) in Node
objects from outside the Node
class, and this is not allowed because those data members were declared in the private section of the Node
class declaration.
We could easily fix this by simply commenting out the private:
statement in the Node
class declaration, thus making all those previously private attributes public. This is not a very elegant way of dealing with the problem because it eliminates all the benefits of having private data members in the first place (i.e. it prevents you or other programmers using your code from unintentionally modifying these variables).
Because Tree
objects will legitimately need to make changes to their Node
objects, we can make the Tree
class a friend of the Node
class. Friend classes are allowed to change the values of private data members. This effectively creates an exception to the privacy rules we established when creating the Node
class.
You need to uncomment two lines in node.hpp to make Tree
a friend of Node
. Uncomment the bold blue lines shown below by removing the //
at the beginning of each line:
#pragma once
#include <string>
#include <vector>
#include <iostream>
//#include "split.hpp"
namespace strom {
<span style="color:#0000ff"><strong>class Tree;</strong></span>
//class TreeManip;
//class Likelihood;
//class Updater;
class Node {
<span style="color:#0000ff"><strong>friend class Tree;</strong></span>
//friend class TreeManip;
//friend class Likelihood;
//friend class Updater;
public:
Node();
~Node();
Node * getParent() {return _parent;}
Node * getLeftChild() {return _left_child;}
Node * getRightSib() {return _right_sib;}
int getNumber() {return _number;}
std::string getName() {return _name;}
//Split getSplit() {return _split;}
double getEdgeLength() {return _edge_length;}
void setEdgeLength(double v);
static const double _smallest_edge_length;
typedef std::vector<Node> Vector;
typedef std::vector<Node *> PtrVector;
private:
void clear();
Node * _left_child;
Node * _right_sib;
Node * _parent;
int _number;
std::string _name;
double _edge_length;
//Split _split;
};
inline Node::Node() {
std::cout << "Creating Node object" << std::endl;
clear();
}
inline Node::~Node() {
std::cout << "Destroying Node object" << std::endl;
}
inline void Node::clear() {
_left_child = 0;
_right_sib = 0;
_parent = 0;
_number = -1;
_name = "";
_edge_length = _smallest_edge_length;
}
inline void Node::setEdgeLength(double v) {
_edge_length = (v < _smallest_edge_length ? _smallest_edge_length : v);
}
}
The first line simply assures the compiler that a class named Tree
exists. Note that we have not included the tree.hpp header file, so the compiler does not have any idea what a Tree
object is at this point. The alternative to adding this line would be to include the file tree.hpp, but that is complicated by the fact that tree.hpp includes node.hpp! The declaration above solves this chicken-and-egg issue, specifying exactly what Tree
is in a situation where the compiler does not need to know anything more about Tree
than the fact that it is the name of a class defined somewhere.
The second line tells the compiler that the class Tree
is allowed to do whatever it likes with the private data members of Node
.
After uncommenting those two lines in node.hpp, running the program should now produce this output:
Starting...
Constructing a Tree
Creating Node object
Creating Node object
Creating Node object
Creating Node object
Creating Node object
Creating Node object
Finished!
Destroying a Tree
Destroying Node object
Destroying Node object
Destroying Node object
Destroying Node object
Destroying Node object
Destroying Node object
Note that 1 Tree
object and 6 Node
objects were created and 1 Tree
object and 6 Node
objects were destroyed when the program finished.
Try changing the tree by adding a node fourth_leaf
as sister to third_leaf
.