Binary Tree (insert and search within radius) - c++

I am writing a binary tree search program but I'm not sure how to add nodes and search through them. The nodes come from a .txt file that is being read with a different file so just assume that already works.
The text file looks like:
Name Location
Old Building 31.2222
New Building 21.2111
Like I said, the program already reads in the file so that's not an issue. However, I have to insert the name and location into the nodes of the binary tree. Then I have to search everything within a range which is where the plus minus comes from.
Side note: my copy constructor may be incorrect as well though it complies properly.
Thanks for the help!
#ifndef BINTREE_HPP
#define BINTREE_HPP
#include <utility>
#include <string>
#include <vector>
class bintree {
// A binary search tree for locations in Lineland.
// Notes:
// - Assume a flat, one-dimensional world with locations from -180 to 180.
// - All locations and distances are measured in the same units (degrees).
public:
// Default constructor
bintree() {
this->root = NULL;
}
// Copy constructor
bintree(const bintree &t) {
this -> root = NULL;
*this = t;
}
// Destructor
~bintree() {
}
// Copy assignment is implemented using the copy-swap idiom
friend void swap(bintree &t1, bintree &t2) {
using std::swap;
// Swap all data members here, e.g.,
// swap(t1.foo, t2.foo);
// Pointers should be swapped -- but not the things they point to.
}
bintree &operator= (bintree other) {
// You don't need to modify this function.
swap(*this, other);
return *this;
}
void insert(const std::string& name, double p) {
// insert node with name and location (p)
}
void within_radius(double p, double r, std::vector<std::string> &result) const {
// Search for elements within the range `p` plus or minus `r`.
// Clears `result` and puts the elements in `result`.
// Postcondition: `result` contains all (and only) elements of the
// tree, in any order, that lie within the range `p` plus or minus
// `r`.
}
private:
struct node
{
node *left;
node *right;
};
node* root;
};
#endif

First, your nodes need to hold the data:
struct node
{
node *left;
node *right;
std::string name; // This is the key for your reasearch
double p; // followed by other data
};
Then you can think to browsing through your tree to insert a new node.
In this example, I assume that you can insert several nodes with the same name.
void insert(const std::string& name, double p) {
node *n = new node; // create a new node
n->name=name; n->p=p; // intialise the data payload
n->left=n->right=nullptr; // and make it a leaf.
if (root==nullptr) // if tree is empty,
root = n; // add the new node.
else { // else find where to insert it
node* t=root;
while (true) {
if (t->name > n->name) { // go to left
if (t->left==nullptr) {
t->left = n;
break;
}
else t=t->left;
}
else if (t->name == n->name) { // insert between current and next
n->right = t->right;
t->right = n;
break;
}
else { // go to right
if (t->right==nullptr) {
t->right = n;
break;
}
else t=t->right;
}
}
}
}
Here a live demo.
Note that I have only answered your insertion question, you still have to do a lot on your own (operator= and copy constructor need review, a destructor needs to be created, etc...)

Related

C++ friend keyword not accessing non-static data member

I need to overload the ostream operator with new functionality for a doubly linked Skip List class.
When I cout the instance of my class, I want it to iterate through my the levels of my skip list, and wherever the head pointer is pointed to a nullptr I want it to print the level name and a status of empty.
Would look something like:
After adding 7
Level: 4 -- empty
Level: 3 -- empty
Level: 2 -- empty
Level: 1 -- empty
Level: 0 -- 7
I need the number of levels to be dynamically entered. I try to assign int level = SkipList::maxLevels_; but I get the error invalid use of non-static data member
I've made the ostream a friend. How can I instruct it access the maxLevels_ data member?
SkipList.h
#include <stdio.h>
#include <iostream>
#ifndef SKIP_LIST_
#define SKIP_LIST_
using namespace std;
class SkipList
{
private:
struct SkipListNode {
// Convenience constructor to create node, set its data, and set all pointers to nullptr
explicit SkipListNode(int data){
data_ = data;
next_ = NULL;
prev_ = NULL;
upLevel_ = NULL;
downLevel_ = NULL;
}
// data for SNode
int data_;
// link to next at same level
SkipListNode* next_;
// link to previous at same level
SkipListNode* prev_;
// link up one level
SkipListNode* upLevel_;
// link down one level
SkipListNode* downLevel_;
};
// maximum # of levels of SkipList, levels are 0 to maxLevels-1
int maxLevels_;
// array of maxLevels_ SkipListNode pointers as head pointers. For example,
// if maxLevels_ == 2, we'd have Heads[0] and Heads[1]. Dynamically allocated
// by constructor.
SkipListNode** heads_;
// array of maxLevels_ SkipListNode pointers as tail pointers.
SkipListNode** tails_;
// given a pointer to a SkipListNode, place it before the given nextNode
void addBefore(SkipListNode* newNode, SkipListNode* nextNode, int level);
// return true 50% of time,
// each node has a 50% chance of being at higher level
bool alsoHigher() const;
public:
//Constructor
SkipList(){maxLevels_ = 1;}
SkipList(int maxLevels);
//Destructor
// virtual ~SkipList();
// return true if successfully added, no duplicates
bool insert(int item);
// item deletion; return true if successfully removed
bool erase(int item);
// return true if found in SkipList
bool contains(int item) const;
friend ostream& operator<<(ostream& os, const SkipList& list){
int level = SkipList::maxLevels_;
while (level >= 0) {
SkipListNode* temp = list.heads_[level];
if (temp == nullptr) {
os << "Level: " << level << "-- empty";
}
else {
while (temp) {
os << temp->data_ << " ";
temp = temp->next_;
}
}
os << endl;
level--;
}
}
};
#endif
SkipList::maxLevels_; refers to the static maxLevels_ member of the SkipList class.
So, if you need maxLevels_ to be the maximum level of all the instances of SkipList you have to declare it as static.
Otherwise in your overloaded friend function you have to use the private member of the list instance.
friend ostream& operator<<(ostream& os, const SkipList& list){
int level = list.maxLevels_;
...

Iterate over elements where not all the elements should have a value

I'm not sure how to describe this. I have to iterate through objects which are connected by pointers with each other, However the first element shouldn't have values stored in its class just the pointer to the next element.
I came up with a small class hierarchy. If I use it like that I have to cast the base class to the derived element. It only seems to work with a dirty cast.
Is there a better solution?
Here the example code:
#include <iostream>
struct Basenode {
Basenode* next;
};
struct Skipnode : Basenode {
Skipnode(int in_key, int in_value);
int key;
int value;
};
Skipnode::Skipnode(int in_key, int in_value)
: key{ in_key }, value{ in_value }
{
}
int main()
try {
Basenode head; // no key and value
Skipnode first(4, 2); // key and value
Skipnode second(8, 2);
Basenode* p = &head;
head.next = &first; // fine
first.next = &second; // fine
// p = p->next->key; // not accesible because is Basenode not derrived Skipnode
std::cout << static_cast<Skipnode*>(p->next)->key; // fine but diryt cast slicing ?
std::cin.get();
}
catch (std::runtime_error& e) {
std::cerr << e.what() << "\n";
std::cin.get();
}
catch (...) {
std::cerr << "unknown error " << "\n";
std::cin.get();
}
Edit: it was asked int the comments why i need this anyway. I think i gave a to limited example.
I need it to implement a skiplist. Many algorithms of it require to start on a element before the first element. The head element. I could make it a normal node and put in dummy values for the values but it didnt seem right. So now i came up with this ugly solution from youre suggestions to start on the head elements.
Heres a snippet with the copy constructor as example.
class Skiplist {
public:
//...
Skiplist(const Skiplist& other); // copy constructor
//...
private:
struct Skipnode; // forward declaration so Basenode can have Skiplist*
struct Basenode { // Empty node, mainly created to represent head element.
// Is there a way to get a empty head with no key / values without using this ?
Basenode(int in_level);
Basenode(const std::vector<Skipnode*>& in_next);
std::vector <Skipnode*> next;
};
struct Skipnode : Basenode { // derived so with Basenode* we can start the iteration of the node on head
Skipnode(value_type val, int in_level);
Skipnode(value_type val, const std::vector<Skipnode*>& in_next);
value_type value; // first key / second mapped type = value
//key_type key;
//mapped_type value;
};
Basenode head{ 0 }; // element before first element containg pointers to all the first elements of each level
//...
};
Skiplist::Skiplist(const Skiplist& other) // copy constructor
:head{ other.head }, top_level{ other.top_level }, random_engine{ other.random_engine }
// on the first level let the other Skiplist present its elements and make a deep copy of them
// now still the higher levels point to the other node so this is fixed in the second part
// then the next level pointers are installed linked to the elements of the new node
{
if (top_level == 0) return; // no elements are present so dont bother to allocate nodes
{
// installment of lowest level, each element is located here
Skipnode* other_node = other.head.next[0];
Basenode* current_position = &head;
while (other_node != nullptr) {
Skipnode* new_node = new Skipnode{ other_node->value,other_node->next };
current_position->next[0] = new_node;
current_position = current_position->next[0];
other_node = other_node->next[0];
}
current_position->next[0] = nullptr;
}
// installment of the other levels
for (size_type curr = 1; curr < top_level; ++curr) {
Basenode* current_position = &head; // the current position of the level[curr]
Skipnode* next_position = current_position->next[curr]; // next position after curr containing still pointers to the other skiplist
Basenode* lowest_position = &head; // lowest level position used to find the new pointers and attach them "behind" current
while (lowest_position != nullptr && next_position != nullptr) {
if (lowest_position->next[0]->value.first == next_position->value.first) { // check by unique key, address of next pos is still of the other skiplist
current_position->next[curr] = lowest_position->next[0]; // lowest is the valid address of new node
current_position = current_position->next[curr];
next_position = next_position->next[curr]; // go to next element of other node
if (next_position == nullptr) { // case end is reached
current_position->next[curr] = nullptr;
current_position = current_position->next[curr];
}
}
else { // forward position of lowest level until other key == next position key
lowest_position = lowest_position->next[0];
}
}
}
}
See here for a basic explanation how a skiplist is organized:
https://en.wikipedia.org/wiki/Skip_list
The whole code is on codereview:
https://codereview.stackexchange.com/questions/197752/non-generic-skip-list-implementation-in-c-version-2
All the things #SomeProgrammerDude is saying, or:
I don't see a need for class BaseNode at all. Why can't we just have (all other things being equal):
SkipNode *head = &first;
...
Or better yet a class called (for example) SkipNodeList that handles all aspects of managing and iterating through a list of SkipNodes.
Of course, this is all a bit silly anyway, just use std::list (or std::forward_list) for this and benefit from all that STL goodness.
Or you can derive from one of these to add your own functionality (such as a mutex to make the list threadsafe or keeping track of the number of elements currently in the list, as suggested by #iMajuscule).
Yes, there is a better way:
Forward-declare Skipnode, and in BaseNode, use a pointer to SkipNode, this way you don't have to cast:
struct Skipnode;
struct Basenode {
Skipnode* next;
};
Also, to illustrate how this design where Skipnode inherits from Basenode could make sense (related to the discussion in the comments), we can imagine having a member in Basenode counting how many elements are next (counting the one in the next member and its successors)

Searching for a node in a tree structure

I need help finding and returning a "node" in a general tree structure. Each node can have more than 2 children so it's not a binary tree. I've been given the following code, this Element object has a list to contain its children, I create one element node pointer in main and using that I have to add and search for children. This is for a school project but I'm not looking for answers (an answer wouldn't hurt). Any advice on how to go about this problem would be much appreciated, thanks!
#pragma once
#include <iostream>
#include <list>
#include <sstream>
using namespace std;
class Element
{
private:
list<Element*> children;
char* _tag;
int _value;
// private methods
public:
// default constructor
Element();
// non-default constructors
Element( char* name); // value is set to -99 if not given
Element(char* name, char* value);
// destructor, must recursively destruct its children
// and release the memory allocated for _tag
~Element();
// ostream operator ( pre-order traversal)
friend ostream& operator << (ostream& out, const Element& E);
void display_xml(); // print out the tree in xml-- format
void addChild( Element* child); // add a child
// Find the first element such that _tag == tag
// returns “this” pointer of this element
Element* findTag( char* tag);
char* getName();
int getValue();
void setName(char* name);
void setValue( int value);
int height(); //b return the height
int size(); // return the size
// other methods
};
this is my best attempt at a solution, it has obvious problems but I'm new to all of this and some explanation on a proper solution, or some sample code would be very helpful!
Element* Element::findTag(char* tag)
{
list<Element*> temp = children;
int s = temp.size();
if(getName() == tag)
{
return this;
}
else
{
for(int i = 0; i < s; i++)
{
findTag((*temp.front()).getName());
temp.pop_front();
}
}
}
I will give you a pseudo-code for searching for a node that has a value val in a tree rooted at root:
find(Node root, val)
if(root.value == val) return root //-- if the recursion found the node we are searching for
else
for every child x of root //-- re-cursing on the children of root
if(find(x, val) != null) return x //-- if one of the calls found the node we are searching for
return null //-- if we did not find the node we want in the sub-tree rooted at root

Nodes added to linked list revert back to NULL after function

I'm reading lines from a file and storing them into linked list.
void add(llist *list, somevalue) {
Node *newnode = (Node *) malloc(sizeof(Node));
newnode->value = somevalue;
newnode->next = list->head;
list->head = newnode;
}
and I call this function from an initialize function which opens the file and reads lines from the file.
void init() {
llist *list = (llist *) malloc(sizeof(llist));
//
//bunch of file i/o codes
//
while (read file until it returns NULL) {
add(list, line);
//if I try to print the values of the list here it works.
}
//Outside the loop, the head is back to NULL
}
And another problem that I realized is the values get concatenated every time I try to print the value. That is to say, the output would be:
First Loop: Tony
Second Loop: Peter
TonyPeter
Third Loop: Mir
PeterMir
TonyPeterMir
How do I fix it so the add function permanently adds the node to the linked list?
Why would the values be jumbled up like that?
----EDITED----
The list is a global variable, and here are some more snippets from the init function. This is the while loop with the problem:
//open file
//initialize & declare pointers
while (1) {
for (i = 0; i < max; i++) {
value[i] = '\0';
}
if (!(fgets(value,max,f))) {
//segfaults if I try to print out the list inside this block.
break;
}
add(list, value);
//the values are well separated in this statement
printf("id is %s\n", list->head->value);
//This print list works, but works weird as shown above.
print_list(list);
}
fclose(f);
//This print list doesn't work, the list is NULL
print_list(list);
And this is the print list function:
void print_users(llist *list) {
ListNode *e;
if (list->head == NULL) {
printf("NO USERS\r\n");
return;
}
e = list->head;
while (e != NULL) {
puts(e->id);
e = e->next;
}
}
So I don't have a good grasp at all on what you're exactly trying to do here, so we can only do but so much. You may consider posting a MCVE. However, I may be able to give you some pointers on building a linked list. I directly copied your add function into a linked list class that I just hurriedly built, and it worked fine, so there may be something else in your llist class that is causing the issue, or it could be something else in your code. The class and a brief description of the class are listed below.
basic node class
Note: I used templates, but you could just as easily remove the template statements and replace T with any type.
template<typename T>
class node {
private:
T data;
node* next;
public:
// The C++11 rule of 5
// Default Constructor
node(T value = T(), node* nxt = nullptr) : data(value), next(nxt) { }
// Copy Constructor
node(const node<T>& src) : data(src.data), next(src.next) { }
// Move Constructor
node(node<T>&& src) : data(src.data), next(src.next) {
src.data = T();
src.next = nullptr;
}
// Copy Assignment Operator
node<T>& operator=(const node<T>& src) {
this->data = src.data;
this->next = src.next;
return(*this);
}
// Move Assignment Operator
node<T>& operator=(node<T>&& src) {
this->data = src.data;
this->next = src.next;
src.data = T();
src.next = nullptr;
}
// Destructor
~node() {};
// Some functions to help with encapsulation
void set_next(node* nxt) {
this->next = nxt;
}
void set_data(T value) {
this->data = value;
}
node* get_next() {
return(this->next);
}
T& get_data() {
return(data);
}
};
linked list class body
Since you're using dynamic memory, you need to make sure you adhere to the rule of 3 or 5 depending on whether or not you're using C++11.
template<typename T>
class llist {
private:
node<T>* head;
public:
llist();
llist(const llist& src);
llist(llist&& src);
llist(~llist);
llist& operator=(const llist& src);
llist& operator=(llist&& src);
void push();
void insert();
};
default constructor
Nothing fancy here.
template<typename T>
llist<T>::llist() : head(nullptr) { }
copy constructor
Since you're using dynamic memory this is crucial
template<typename T>
llist<T>::llist(const llist& src) {
node<T>* tmp = src.head;
while(tmp) {
this->push(tmp->get_data());
}
}
move constructor
template<typename T>
llist<T>::llist(llist&& src) {
// delete current nodes
node<T>* tmp = this->head;
while(tmp) {
tmp = head->get_next();
delete head;
head = tmp;
}
// steal the sources list
this->head = src.head;
src.head = nullptr;
}
destructor
template<typename T>
llist<T>::~llist() {
node<T>* tmp = this->head;
while(tmp) {
tmp = head->get_next();
delete head;
head = tmp;
}
}
copy assignment operator
template<typename T>
llist& llist<T>::operator=(const llist<T>& src) {
node<T>* tmp = src.head;
while(tmp) {
this->push(tmp->get_data());
}
return(*this);
}
move assignment operator
template<typename T>
llist& llist<T>::operator=(llist<T>&& src) {
node<T>* tmp = this->head;
while(tmp) {
tmp = head->get_next();
delete head;
head = tmp;
}
this->head = src.head;
src.head = nullptr;
return(*this);
}
push member
this is essentially opposite of your add member.
template<typename T>
void llist<T>push(T data) {
node<T>* new_node = new node<T>(data);
if(this->head) {
node<T>* tmp = this->head;
while(tmp->get_next()) {
tmp = tmp->get_next();
}
tmp->set_next(new_node);
} else {
this->head = new_node;
}
}
insert member
This is essentially your add member.
template<typename T>
void llist<T>insert(T data) {
node<T>* new_node = new node<T>(data, this->head);
this->head = new_node;
}
I don't know if this will help, and you probably already have and know most of this, but I hope it helps.
In this code, it would appear that you attempted to 'malloc' space for a "llist" user defined object.
void init() {
llist *list = (llist *) malloc(sizeof(llist));
//
//bunch of file i/o codes
//
while (read file until it returns NULL) {
add(list, line);
//if I try to print the values of the list here it works.
}
//Outside the loop, the head is back to NULL
}
First, you tagged this as C++. In C++, you simply must use new and delete. The C++ compiler does not associate "malloc" with the ctor / dtor of your user created object called "llist". And I assure you that you really do want to create these two methods, even when each are simple. Really.
On the other hand, the C++ compiler does provide New and Delete, and will automatically invoke the ctor and dtor when appropriate for both dynamic variables (in heap), and automatic variables (on stack). The compiler will not support this with malloc.
Second, your function init() does not return or otherwise deliver the value of the automatic variable you named "list" to any other scope. (Typically, a list lifetime exceeds the life of any function that uses or creates it.)
So your object "list" only exists within the scope of the function init(), within the lifetime of init(). Not very useful.
So the handle to the list of 'malloc'ed things is lost, no longer accessible to anything else. After init(), where did you plan for the listHead to reside?
Even if you used new (and delete) the code still does not deliver the listHead anywhere.
To further your program, you need perhaps 1 of 2 things:
1) a return (from the function) of the "list" handle (I've been calling it "listHead" as you intended, right?)
llist* init() {
llist *listHead = ...
return(listHead);
}
OR
2) a parameter reference which your init function changes. This places the list head outside of init().
void init( llist** listHead) {
llist *list = ...
*listHead = list;
}
You might look into, and take hints from std::list, which has 40+ methods, though you might only need 10. For the methods you plan to implement, you should strive to conform to and use similar names and parameters.
Perhaps you meant to use a class data attribute with the label list (it is quite difficult to imagine this from what you provided). In this case, you should distinguish data attributes names to help you remember what it is, and that it has a different scope. For instance, I would use m_listHead. The prefix m_ (or often, simply the one char prefix '_') simply indicates to the reader that this symbol is a data attribute of a class. This idea is a common c++ idiom, and not enforced by the compiler, but is often part of a coding-standard.
Good luck.

Copy constructor and dynamic allocation

I would like to ask you how to write a copy constructor (and operator = ) for the following classes.
Class Node stores coordinates x,y of each node and pointer to another node.
class Node
{
private:
double x, y;
Node *n;
public:
Node (double xx, double yy, Node *nn) : x(xx), y(yy), n(nn) {}
void setNode (Node *nn) : n(nn) {}
...
};
Class NodesList (inherited from std:: vector) stores all dynamically allocated Nodes
class NodesList : public std::vector<Node *>
{}
The main program:
int main()
{
Node *n1 = new Node(5,10,NULL);
Node *n2 = new Node(10,10,NULL);
Node *n3 = new Node(20,10,NULL);
n1->setNode(n2);
n2->setNode(n3);
n3->setNode(n2);
NodesList nl1;
nl1.push_back(n1);
nl1.push_back(n2);
nl1.push_back(n3);
//Copy contructor is used, how to write
NodesList nl2(nl1);
//OPerator = is used, how to write?
NodesList nl3 = nl1;
}
I do not want to create a shallow copy of each node but a deep copy of each node. Could I ask you for a sample code with copy constructor?
Each node can be pointed more than once. Let us have such situation, when 3 nodes n[1], n[2], n[3] are stored in the NodesList nl1:
n[1] points to n[2]
n[2] points to n[3]
n[3] points to n[2]
A] Our copy constructor process the node n[1]. It creates a new object n[1]_new represented by the copy of the old object n[1]_old. The node n[2] pointed from n[1]_old still does not exist, so n[2]_new must be also created... The pointer from n1_new to n2_new is set.
B] Then second point n[2] is processed. It can not be created twice, n[2]_new was created in A]. But pointed node n[3] does not exist, so the new object n[3]_new as a copy of an old object n[3]_old is created. The pointer from n2_new to n3_new is set.
C] Node n[3]_new has already been created and n[2]_new. The pointer from n3_new to n2_new is set and no other object will be created...
So the copy constructor should check whether the object has been created in the past or has not...
Some reference counting could be helpful...
There is my solution of the problem. A new data member n_ref storing a new verion of the node n was added:
class Node
{
private:
double x, y;
Node *n, *n_ref;
public:
Node (double xx, double yy, Node *nn) : x(xx), y(yy), n(nn) {n_ref = NULL;}
Node * getNode() {return n;}
Node * getRefNode () {return n_ref;}
void setNode (Node *nn) {this->n = nn;}
void setRefNode (Node *nn) {this->n_ref = nn;}
The copy constructor creates a shallow copy of the node:
Node (const Node *node)
{
x = node->x;
y = node->y;
n = node->n;
n_ref = node->n_ref;
}
The copy constructor for NodesList
NodesList::NodesList(const NodesList& source)
{
const_iterator e = source.end();
for (const_iterator i = source.begin(); i != e; ++i) {
//Node* n = new Node(**i);
//Node n still has not been added to the list
if ((*i)->getRefNode() == NULL)
{
//Create node
Node *node = new Node(*i);
//Add node to the list
push_back(node);
//Set this note as processed
(*i)->setRefNode(node);
//Pointed node still has not been added to the list
if ((*i)->getNode()->getRefNode() == NULL)
{
//Create new pointed node
Node *node_pointed = new Node ((*i)->getNode());
//Add node to the list
push_back(node_pointed);
//Set pointer to n
node->setNode(node_pointed);
//Set node as processed
((*i)->getNode())->setRefNode(node_pointed);
}
//Pointed node has already been added to the list
else
{
//Set pointer to node n
node->setNode((*i)->getRefNode());
}
}
//Node n has already been added to the list
else
{
//Get node
Node * node = (*i)->getRefNode();
//Pointed node still has not been added
if ((*i)->getNode()->getRefNode() == NULL)
{
//Create new node
Node *node_pointed = new Node ((*i)->getNode());
//Add node to the list
push_back(node_pointed);
//Set pointer to n
node->setNode(node_pointed);
//Set node as processed
((*i)->getNode())->setRefNode(node_pointed);
}
//Pointed node has already been added to the list
else
{
//Set pointer to n
node->setNode((*i)->getNode()->getRefNode());
}
}
}
}
Perform a shallow copy in NodeList::NodeList(const NodeList&) and you don't have to worry about cycles breaking the copy operation. Disclaimer: the following is untested, incomplete and may have bugs.
class NodeList {
private:
typedef std::vector<Node*> Delegate;
Delegate nodes;
public:
NodeList(int capacity=16) : nodes() { nodes.reserve(capacity); }
NodeList(const NodeList& from);
virtual ~NodeList();
NodeList& operator=(const NodeList& from);
/* delegated stuff */
typedef Delegate::size_type size_type;
typedef Delegate::reference reference;
typedef Delegate::const_reference const_reference;
typedef Delegate::iterator iterator;
typedef Delegate::const_iterator const_iterator;
size_type size() const { return nodes.size(); }
iterator begin() { return nodes.begin(); }
const_iterator begin() const { return nodes.begin(); }
iterator end() { return nodes.end(); }
const_iterator end() const { return nodes.end(); }
// ...
};
NodeList::NodeList(const NodeList& from)
: nodes(from.size()), flags(NodeList::owner)
{
std::map<Node*, Node*> replacement;
Delegate::const_iterator pfrom;
Delegate::iterator pto;
// shallow copy nodes
for (pfrom=from.begin(), pto=nodes.begin();
pfrom != from.end();
++pfrom, ++pto)
{
replacement[*pfrom] = *pto = new Node(**pfrom);
}
// then fix nodes' nodes
for (pto = nodes.begin(); pto != nodes.end(); ++pto) {
(*pto)->setNode(replacement[(*pto)->getNode()]);
}
}
NodeList::operator=(const NodeList&) can use the copy-swap idiom, the same as Tronic's Node::operator=(const Node&).
This design has a potential memory leak in that a copied NodeList is (initally) the only place that references its nodes. If a temporary NodeList goes out of scope, a poor implementation will leak the Nodes the list contained.
One solution is to proclaim that NodeLists own Nodes. As long as you don't add a Node to more than one NodeList (via NodeList::push_back, NodeList::operator[] &c), NodeList's methods can delete nodes when necessary (e.g. in NodeList::~NodeList, NodeList::pop_back).
NodeList::~NodeList() {
Delegate::iterator pnode;
for (pnode = nodes.begin(); pnode != nodes.end(); ++pnode) {
delete *pnode;
}
}
void NodeList::pop_back() {
delete nodes.back();
nodes.pop_back();
}
Another solution is to use smart pointers, rather than Node*. NodeList should store shared pointers. Node::n should be a weak pointer to prevent ownership cycles.
I would just use std::list<Node> instead of NodesList. Well, let's code...
NodesList::NodesList(const NodesList& source)
{
const_iterator e = source.end();
for (const_iterator i = source.begin(); i != e; ++i) {
Node* n = new Node(**i);
push_back(n);
}
}
Apparently each Node is only allowed to point to another Node in the same list? Otherwise the "deep copy" of a list needs more definition. Should it not be connected to the original NodeList? Should it not be connected to any original Node? Are copies of Nodes not in the list being copied added to some other list or free-floating?
If all the Node-to-Node pointers are constrained within the NodeList, then perhaps you should store indexes instead of pointers, then no special handling is required.
You should not inherit from standard library containers (because they lack virtual destructors). Instead, include them as member variables in your classes.
Since you want a deep copy, you need these: (rule of three)
Node(Node const& orig): x(orig.x), y(orig.y), n() {
if (orig.n) n = new Node(*orig.n);
}
Node& operator=(Node const& orig) {
// The copy-swap idiom
Node tmp = orig;
swap(tmp); // Implementing this member function left as an exercise
return *this;
}
~Node() { delete n; }
A better idea might be to avoid using pointers entirely and just put your nodes in a suitable container.