I'm working on a class hierachy, where an object of a class may contain several objects of the same class.
This would lead to a tree structure:
class myClass
{
myClass *parent;
std::vector<std::unique_ptr<myClass> > childs;
/*
* here are some more attributes which describe a myClass object
* but are not related to the tree structure.
* std::string name; for example
*/
public:
//constructors...
//tree management functions
//some more members...
}
However, after thinking about it for a while, i think this is bad practise.
In this approach, myClass not only needs to have members related to its actual behaviour but also for managing the tree, like searching or inserting childs. This would be a lot of different functionality in one single class.
And, as we are lazy programmers, I don't like to re-invent the wheel.
There are some tree-containers out there, for example the well-known "tree.hpp".
Why not using this container for storing the myClass objects?
Well, problem is, some members ob myClass require access to its parents.
Imagine a member-function like getFullName(), which returns not only the "name"-attribute but a complete path to the actual object(all the parent's names). So this function would need to iterate trough all parent nodes until root is reached.
I'm not sure how i can achieve this using tree.hpp or similar containers.
Does myClass then need to store a pointer to the tree-node which contains it?
But I cannot think of an example where an object has information about the container containing it. An object of a class should not know anything of "being contained". Or am I wrong?
Maybe my first approach (myClass does also the tree management) is even OK?
OK, maybe i should ask a simplier question:
What is a good way to let an object know its own position within a container, e.g. a tree?
What is a good way to let an object access its parent in without storing to much (redundant) information in the object itself?
First of all, this is wrong:
class myClass
{
myClass *parent;
std::vector<std::unique_ptr<myClass> > childs;
};
Your children should be stored as a vector of shared_ptr, and the parent should be a weak_ptr.
class myClass
{
std::sweak_ptr<myClass> parent;
std::vector<std::shared_ptr<myClass> > children;
};
However, it looks like what you want is a template tree container. So your class should look like this:
class myClass
{
/*
* here are some more attributes which describe a myClass object
* but are not related to the tree structure.
* std::string name; for example
*/
public:
//constructors...
//tree management functions
//some more members...
};
And the tree container should be something like this (conceptually):
template <typename T>
class tree
{
std::sweak_ptr<tree> parent;
std::vector<std::shared_ptr<tree> > children;
T value;
};
This tree can be generic and hold different types of objects, including myClass.
Related
I have a tree-like data structure that I've set up like this:
class Root; // forward declaration
class Tree {
public:
void addChildren(Root &r, ...) { childA = r.nodeSpace.allocate(); ... }
// tons of useful recursive functions here
private:
Tree *childA, *childB, *childC;
Tree *parent;
int usefulInt;
};
class Root : public Tree {
friend class Tree; // so it can access our storage
public:
private:
MemoryPool<Tree> nodeSpace;
};
I really like this structure, because
I can call all the recursive functions defined on Tree on Root as well, without having to copy-paste them over.
Root owns the storage, so whenever it passes out of scope, that's how I define the tree as no longer being valid.
But then I realized a problem. Someone might inadvertently call
Tree *root = new Root();
delete root; // memory leak! Tree has no virtual destructor...
This is not an intended usage (any ordinary usage should have Root on the stack). But I am open to alternatives. Right now, to solve this, I have three proposals:
Add virtual destructor to Tree. I would prefer not doing this because of the overhead as the tree can have many, many nodes.
Do not let Root inherit from Tree but instead have it define its own Tree member. Creates a little indirection, not too terrible, can still call the tons of useful recursive functions in Tree by doing root.tree().recursive().
Forbid assignments like Tree *root = new Root();. I have no idea if this is even possible or discouraged or encouraged. Are there compiler constructs?
Something else?
Which one of these should I prefer? Thank you very much!
The root node class (or any other node class) should not be an interface class. Keep it private and then inheritance without dynamic polymorphism (virtual) is not dangerous because the user will never see it.
Forbid assignments like Tree *root = new Root();. I have no idea if this is even possible or discouraged or encouraged. Are there compiler constructs?
This would be done by having Root inherit from Tree as a private base class.
I finished writing an AVL tree, and one of the things that bothered me when programming it is deciding which methods belong to which class:
template <class ValueType,class CompareFunction>
class avlTree{
class avlTreeException{};
public:
class ElementDoesntExist : public avlTreeException{};
class EmptyTree : public avlTreeException{};
class ElementAlreadyExists : public avlTreeException{};
private:
class Node{
friend class avlTree;
ValueType* data;
Node *sonA,*sonB,*dad;
int height,balance;
private:
CompareFunction compare;
int treeSize;
Node* root;
};
(I removed the public\private methods to save space).
For some methods I think I made the right choice: update is a method of Node (updates height,etc).
Insert/remove are functions of the tree.
But for example the function destroyNodeTree(Node*) which is used by the tree destructor. What I did is to have destroyNodeList() call destroyNodeTree(root)
template <class ValueType,class CompareFunction>
void avlTree<ValueType,CompareFunction>::avlTree::destroyNodeTree(Node* rooty) {
if(!rooty){
return;
}
Node *A=rooty->sonA,*B = rooty->sonB;
destroyNodeTree(A);
destroyNodeTree(B);
}
However, I could have made destroyNodeTree() a method of Node, and call it on the root from the destructor (it would be implemented in the same way).
I had a similar issue deciding where the method findNode(const ValueType&) should go, meaning it obviously is a public method of tree, but should I create a method for Node with the same name and have the tree function call the node method on the root? Is it even acceptable to have a public function and an inner class method with the same name?
In my opinion it's better to have it as a method of nodes because that gives more flexibility (I'll be able to search for a node only under a certain node), but on the other hand that means that the method either needs to create an instance of class compare, or have each node keep a copy of an instance, or have class compare as a static function. Each of those has a disadvantage in my opinion though: creating an instance can be costly, keeping a copy can be costly, and forcing the user to make the function static doesn't seem right to me (but I'm horribly inexperienced so fix me if I'm wrong).
In any case I eventually made findNode a treeFunction only and not a method (the HW assignment didn't need the tree to able to search from a specific node so it doesn't make any difference there) but I don't want to write bad code.
To conclude, how do we decide where to save performance,memory,flexibility of the user (would he rather be able to search from any node or create nonstatic compare functions?)
In c++, We all know that this code is wrong:
class Node {
public:
Node node;
};
However, declaring a vector like this is right, WHY?
class Node {
public:
std::vector<Node> nodeVec;
};
It works just fine in the C++14 compiler I'm using. And it provides a very convenient pattern for representing an arbitrarily large complex tree-shaped data structure inside a “single” instance. A nice property is that all storage management for the entire tree is handled automatically by std::vector. For example you can have a Node member variable in a class Foo and not worry about deleting the tree when you are done with the enclosing Foo instance.
As described elsewhere (How can I declare a member vector of the same class?) it is not a problem for the compiler because the size of std::vector<Node> itself is independent of the size of Node, because std::vector is a fixed size header with a pointer to a separate block of managed storage on the heap.
If you have a generic Node that store ints, float or Objects of a certain type, how could you store generic objects in your node?
typedef struct node{
Dog data;
node* next;
}*nodePtr;
This node stores Dog objects... how could I store generic objects?
One idea I have is to have Dog objects and all other objects inherit from a more general Object class. Good way to go other than using templates?
C++ offers the template<> for generics:
template<typename T>
struct node {
T data;
node<T> *next;
}
Make a template, like this:
template<typename T>
struct Node
{
T data;
Node<T> *next;
};
A good resource to find information on templates can be e.g. the Wikipedia.
One idea I have is to have Dog objects and all other objects inherit from a more general Object class. Good way to go?
If the types all have something in common, create a common base type for them. If not, then don't.
Don't make the types derive from a common base just because you want to store them all in the same container. You'd have it backwards. If you want to store all the types in the same container, they should have something in common already. Otherwise your container is just a sequence of bits. There would be nothing it could do that wouldn't be better done by separate containers for each type. For example, you couldn't iterate through the container and call a method on each element, because there wouldn't be a method that all the elements have!
You said,
Great answer, but I'm looking to do it through OO principles.
One of the basic principles of OO, IMO, is that all your classes should be meaningful. This doesn't mean they have to correspond to concrete objects, or even contain any implementation, but they do have to at least contain some interface. A generic Object class in C++ is not meaningful. Don't create one.
I need a little assistance with high level design of this program.
I am manually creating class objects based on an XML description. It has nodes which define settings of a piece of hardware. The trouble is the nodes in the XML can simply point to other nodes by name. To be able to find the address of these node objects quickly based on name, I am using a map. This is simplified view:
class XMLfile
{
public:
Node* GetNode(const string FeatName); //Searches through "nodes" map to get pointer to Node
private:
map<std::string, Node*> nodes;
};
Then I define nodes as follows:
class Node
{
public:
virtual int GetValue();
virtual int Load(XML_type);
};
Then depending on the exact "type" of node, I create a few derived classes with additional variables.
class Integer: public Node
{
int IntegerSpecificVar;
int GetValue();
int Load(XML_type);
private:
int Value;
string p_Value;
};
The XML can be one of the two:
<Value>5</value> Which my object load will load 5 into int Value.
or
<p_Value>NodeName</p_Value> Which my object loader will load the string into p_Value.
Depending on the object in the XML, the GetValue() returns the int Value, or a pointer to a string with a different nodename. If its a pointer, I need to get the pointer to that node and then call GetValue on that object. What I really want to do is call XML::GetNode(nodename") but this is out of context.
One idea to get my Node objects access to that map is to create the class Node which is derived from the XMLfile:
class Node: public XMLfile
then I get circular references since it has not loaded yet. If I switch the order between class Node and class XMLfile, then XMLfile doesn't know what a Node class/type is. Note this is all in one big file for now.
IF anyone can offer general suggestions how to better arrange this it would be appreciated.
It's bad idea to think of a Node as an specified case of XMLfile, so don't derive one from the other. Inheritance means the relationship of A 'is-a' B.
Quick and possibly dirty solutions:
make the map a parameter of GetValue, or
store a (const) reference or pointer to the map inside Node.