Graph represented by an adjacency matrix in C++ I'm working on my data structures knowledge and wanted to create a graph with a small DFS driver which simply prints the nodes as it visits them. Here's my graph class:

#ifndef GRAPH_H_
#define GRAPH_H_

#include &lt;stack&gt;
#include &lt;random&gt;
#include &lt;iterator&gt;
#include &lt;unordered_map&gt;
#include &lt;unordered_set&gt;

template &lt;typename T&gt;
class Graph {
  public:
    Graph(bool directed) : directed{directed}, adj_matrix{} {}
    ~Graph() {}
    void add_edge(const T&amp; start, const T&amp; end) {
      adj_matrix[start][end] = true;
      adj_matrix[start][start] = false;
      adj_matrix[end][end] = false;
      adj_matrix[end][start] = directed ? false : true;
    }

    void DFS(T const *start=nullptr) const {
      if(!adj_matrix.size())
        return;
      if(start==nullptr) {
        DFS_helper(get_random_node());
      } else {
        DFS_helper(*start);
      }
    }

    void DFS_helper(const T&amp; start) const {
      std::unordered_set&lt;T&gt; visited;
      std::stack&lt;T&gt; s;
      s.push(start);
      while(!s.empty()) {
        T top = s.top();
        s.pop();
        // If top is unvisited
        if(visited.find(top) == std::end(visited)) {
          // visit it
          visited.insert(top);
          std::cout &lt;&lt; top &lt;&lt; ' ';
          for(const auto&amp; adj_node: get_adjacent_nodes(top)) {
             s.push(adj_node);
          }
        }
      }
      std::cout &lt;&lt; '\n';
    }

  private:
    std::vector&lt;T&gt; get_adjacent_nodes(const T&amp; v) const {
        std::vector&lt;T&gt; adj;
        for(const auto&amp; pair: adj_matrix.at(v)) {
            T neighbor = pair.first;
            bool is_neighbor = pair.second;
            if(is_neighbor) {
                adj.push_back(neighbor);
            }
        }
        return adj;
    }
    T get_random_node() const {
      auto random_it = std::next(std::begin(adj_matrix), std::rand() % (adj_matrix.size()-1));
      return random_it-&gt;first;
    }
    const bool directed;
    std::unordered_map&lt;T, std::unordered_map&lt;T, bool&gt;&gt; adj_matrix;
};

#endif //GRAPH_H_


And here's my driver (inspired from this Wikipedia image):

#include &lt;iostream&gt;
#include "graph.h"

int main() {
  bool directed = false;
  Graph&lt;char&gt; g(directed);
  g.add_edge('A', 'B');
  g.add_edge('B', 'D');
  g.add_edge('A', 'E');
  g.add_edge('E', 'F');
  g.add_edge('A', 'C');
  g.add_edge('C', 'G');
  g.add_edge('B', 'F');

  char a = 'A';
  g.DFS(&amp;a);
}


I'm interested in any tips or recommendations!
Your code looks reasonable, yet I have one suggestion: decouple algorithms from output: 

std::vector&lt;T&gt; DFS(T const *start=nullptr) const {
    if(!adj_matrix.size())
        return std::vector&lt;T&gt;();
    if(start==nullptr) {
        return DFS_helper(get_random_node());
    } else {
        return DFS_helper(*start);
    }
}

std::vector&lt;T&gt; DFS_helper(const T&amp; start) const {
    std::unordered_set&lt;T&gt; visited;
    std::unordered_map&lt;T, T*&gt; parents {{start, nullptr}};
    std::stack&lt;T&gt; s;
    std::vector&lt;T&gt; nodes;
    s.push(start);
    while(!s.empty()) {
        T top = s.top();
        s.pop();
        // If top is unvisited
        if(visited.find(top) == std::end(visited)) {
            // visit it
            visited.insert(top);
            nodes.push_back(top);
            for(const auto&amp; adj_node: get_adjacent_nodes(top)) {
                s.push(adj_node);
                parents[adj_node] = &amp;top;
            }
        }
    }
    return nodes;
}


Suppose what happens if your friend needs to run your DFS on a large graph. Most definitely, he or she will be overwhelmed with all the output. So, let the DFS return silently a list of visited nodes; only after that print the nodes if need be.