Question

I am writing program which has a multitude of Directed Graph helper functions in order to gain a deeper understanding of C++. One of the central objects is called a Node which has member functions to help with calculating travel distance between nodes. I am trying to gain a better understanding of using C++ templates in OOP design.

Here is a quick snapshot of the Node class

class Node {

    friend void swap(Node & first, Node & second) {
        using std::swap;
        swap(first.name, second.name);  
    }

public:

    Node(std::string val);

    Node(const Node & copy);

    Node & operator = (Node copy) {

        swap(*this, copy);
        return *this;

    }

    bool operator < (Node & rhs) const {
        return (size < rhs.size);
    }

    bool operator > (Node & rhs) const {
        return (size > rhs.size);
    }

    bool insertEdge(Node * dest, int distToNode);

    // I'd like for this return type to not be tied to an int
    // Especially if weights were represented as floats or doubles
    int findTravelDistance(Node * const & toNode) const;
    int findTravelDistance(std::queue<Node *> * const & nodeRoute) const;

    // Mutators
    void setNodeName(const std::string nameToSet);
    std::string getNodeName() const;

    void setNodeSize(const int size);
    int getNodeSize() const;

    // Misc
    void toString() const;

    // Constants
    static const bool ALLOW_CIRCULAR;

    ~Node();

protected:


private:
    int size;
    std::string name;
    // Here int represents the weight of the edge. I would like it to be able to be
    // declared as an int, float, long, or double etc...
    std::map<Node *, int> * travelEdges;

}; // end class

} // end namespace

As I build this class to include more functionality, I find myself struggling with how to make my functions more adaptable. For instance, look at the findTravelDistance functions.

What I would like to do is have the return type representing weight be type agnostic and the ordered map data structure's value to be type agnostic. As it is currently implemented, a user can only declare a type int for the weight. I realize I could embark upon function overloading. But, I feel this would be too redundant and an obvious violation of the DRY principle. If I were to have to change how this function works, I would have to change it for every overload. So my gut instinct tells me I should use C++ templates. Since I am new to templates, I am struggling with where to declare it. If I make my find functions template functions and just return the generic type..

template<class T>
T findTravelDistance(std::queue<Node *> * const & nodeRoute) const;

That will solve my problem there. But, it doesn't fix the issue where the underlying map data structure representing edges can only hold ints. My next thought was to declared a class template..

template<class T>
class Node { ... }

But this also seemed odd to me. This would mean declaration and initialization would look something like

Node<float> * n = new Node<float>("N");

If I were a user of my program, I would not immediately associate Node with the float type representing edge weights.

So what is the best or appropriate usage of a template in this case? Or is using a template even the correct path here? It is possible my class design is flawed to begin with and not very C++'esk. Any feedback here is much appreciated.

Was it helpful?

Solution

This is very clean code :). Welcome to C++!

I believe what you want to do is use a template variable to hold your edge weights. How about something like the following:

using std::swap;
template<class Distance>
class Node {

friend void swap(Node & first, Node & second) {  
    swap(first.name, second.name);  
}
public:

Node(std::string val);

Node(const Node & copy);

Node & operator = (Node copy) {

    swap(*this, copy);
    return *this;

}

bool operator < (Node & rhs) const {
    return (size < rhs.size);
}

bool operator > (Node & rhs) const {
    return (size > rhs.size);
}

bool insertEdge(Node * dest, Distance distToNode);

// I'd like for this return type to not be tied to an int
// Especially if weights were represented as floats or doubles
Distance findTravelDistance(Node * const & toNode) const;
Distance findTravelDistance(std::queue<Node *> * const & nodeRoute) const;

// Mutators
void setNodeName(const std::string nameToSet);
std::string getNodeName() const;

void setNodeSize(const Distance size);
int getNodeSize() const;

// Misc
void toString() const;

// Constants
static const bool ALLOW_CIRCULAR;

~Node();

private:
 int size;
std::string name;
std::map<Node *, Distance> * travelEdges;

}; // end class

As a bonus, I've moved your using declarations to the top of the class. Generally these go at the top of the file. You also might benefit from taking a look at the holy scripture that is the Parashift C++ FAQ, particularly the section on const correctness. Your comparator methods, for example, should have const Node& parameters.

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top