Create the Tree class

The next step is to create a C++ class encapsulating the notion of a phylogenetic tree.

Create the Tree class in a file named tree.hpp using the same process you used to create node.hpp.

If using Mac, command-click on the project folder in the Project Navigator and choose New File… from the popup menu (be sure to name the new file tree.hpp and check the strom project).

Your IDE (Visual Studio Community on Windows or Xcode on Mac) will produce some default code in any new file it creates, but we will always replace the default code with our own code. Replace everything in the tree.hpp file now with the text below.

Contents of tree.hpp

#pragma once

#include <memory>
#include <iostream>
#include "node.hpp"

namespace strom
    {

    class TreeManip;
    class Likelihood;

    class Tree
        {

        //friend class TreeManip;
        //friend class Likelihood;

        public:

                                        Tree();
                                        ~Tree();

        private:

            void                        clear();

            bool                        _is_rooted;
            Node *                      _root;
            unsigned                    _nleaves;
            Node::PtrVector             _preorder;
            Node::Vector                _nodes;
      
        public:
        
            typedef std::shared_ptr< Tree > SharedPtr;
        };

    inline Tree::Tree()
        {
        std::cout << "Constructing a Tree" << std::endl;
        clear();
        }

    inline Tree::~Tree()
        {
        std::cout << "Destroying a Tree" << std::endl;
        }

    inline void Tree::clear()
        {
        _is_rooted = false;
        _root = 0;
        _nodes.clear();
        _preorder.clear();
        }

    }

Explanation of tree.hpp

Headers

Just after the pragma directive (explained earlier) are three lines that include header files. The contents of each of these header files (headers, for short) are effectively dumped into this file at the point where they are invoked. The <memory> header file provides the definition of std::shared_ptr (explained below). The <iostream> header file provides the definition of std::cout, which is used in the constructor and destructor function bodies. Finally, The "node.hpp" header file provides our definition of the Node class. The headers that are surrounded by <angled brackets> are system headers, while those surrounded by "quotes" are provided by you. This convention helps the compiler, which will generally look for system header files in a different place than user-provided headers.

Inline keyword

You may note that the member functions are all labeled with the inline keyword. The inline keyword is a friendly request made to the compiler that you would like the function body to be simply copied into the place where it is called, which is often more efficient than a function call. The compiler gets to decide, however, whether the body of a particular function will actually be inlined at a particular place in the code.

Constructor and destructor

The class declares a public constructor and a public destructor. As we saw for the Node class, the constructor is responsible for initializing the memory set aside to store a new Tree object, and the destructor is in charge of cleaning up the object before the memory for the object is released. Our Tree constructor calls the member function clear() to initialize the data members of the Tree class. I have (temporarily) placed code in both constructor and destructor to write output. This output will allow us to easily see when a Tree object is being constructed or destructed. We will remove these output lines after we test the class.

Member functions

The member function clear() is provided to restore a Tree object to its just-constructed state.

Data members

A boolean data member _is_rooted indicates whether the Tree object should be considered rooted or unrooted. A Node pointer _root will point to the Node object serving as the root node of the Tree object. An unsigned integer _nleaves stores the number of tip nodes (leaves) in the tree. Finally, two standard vector data members store nodes. The vector _nodes stores the node objects themselves, while the vector _preorder stores pointers to the node objects in _nodes.

Shared pointer

SharedPtr type is defined that represents a shared pointer (also known as a smart pointer) to our Tree class. Smart pointers keep track of how many references there are to a particular object. Once no other object is holding on to a reference (i.e. is pointing to) an object, the smart pointer takes care of deleting the object automatically. This makes memory management much easier because you, as the programmer, do not have to remember to delete objects as long as you always manage them via smart pointers.

Note that the type name we’ve defined is just SharedPtr, which is not specific to Tree. The fact that this type is defined within the Tree class declaration, however, means that we will always know the object referred to by this SharedPtr because to use it we will have to qualify it by the class name: Tree::SharedPtr tree.

Friends

As for the Node class, the TreeManip and Likelihood classes will need to have access to the private member functions and data members of the Tree class. Because TreeManip and Likelihood have not yet been created, these friend declarations are currently commented out.