C++ linked list Segmentation fault and valgrind errors - c++

i need to code a linked list for university in c++, mostly to practice coding iterators.
I tested it with some basic cases and it works but after i pass it in valgrind and the test server for the program i get a list of different errors. Maybe somebody can help me not to despair.
(At the end i will append the error list)
template <typename T = float>
class ForwardList
{
struct Node
{
/// Constructs a Node from a data value and a link to the next element.
Node(const T &data, Node *next) : data{data}, next{next} {}
/// A Node owns all nodes after it, so it deletes them on destruction
~Node() { delete next; }
//Performs a deep copy of the Node and all Nodes after it. Bad practice but we got it like that
Node *clone() const
{
if (next == nullptr)
{
return new Node{data, nullptr};
}
else
{
return new Node{data, next->clone()};
}
}
T data;
Node *next;
};
public:
ForwardList() : head(nullptr) {}
/// Copy constructor performs a deep copy of the other list's Nodes
ForwardList(const ForwardList &other)
{
head = other.head->clone();
}
/// Destructor makes sure that all Nodes are correctly destroyed
~ForwardList()
{
while (head->next != nullptr)
{
Node *tmp = head;
head = head->next;
delete tmp;
}
delete head;
}
/// Copy assignment operator uses the copy-and-swap idiom to make a safe
/// assignment
ForwardList &operator=(ForwardList other)
{
swap(*this, other);
return *this;
}
/// Add an element to the front of the list.
void push_front(const T &value)
{
std::cout << "Num: " << numberOfNodes << std::endl;
Node *item = new Node(value, nullptr);
if (head==nullptr)
{
head = item;
}else
{
item->next=head;
head = item;
}
numberOfNodes++;
}
/// Remove the first element of the list. Calling this function on an empty
/// list is undefined behavior. When implementing this function, be careful
/// to delete the one and only the one element that is removed.
void pop_front()
{
Node *item;
item = head->next;
delete head;
head = item;
numberOfNodes--;
}
/// Get a reference to the first element of the list
/// (const and non-const version)
T &front()
{
return head->data;
}
const T &front() const
{
return head->data;
}
/// Return true is the list is empty
bool empty() const
{
return numberOfNodes == 0 ? true : false;
}
std::size_t size() const
{
return numberOfNodes;
}
friend void swap(ForwardList &l, ForwardList &r)
{
Node *tmp = l.head;
l.head = r.head;
r.head = tmp;
}
private:
Node *head;
size_t numberOfNodes = 0;
};
And now the fun part (i will put it on pastebin because its pretty long):
https://pastebin.com/4JAKkJtP

Your issue is that ~Node tries to delete its next, and you also try to walk the list in ~ForwardList. By deleting ~Node(), you let ForwardList handle cleanup and everything works.
The clue here is that valgrind reported use after free, meaning something was deleting a pointer twice. That was a clue to look at everything that deletes a Node* (or really, delete in general).

Related

getting segmentation fault on this code for deletion of node in linked list

I'm trying to write a function for the deletion of a node in a linked list , given a double pointer to the head and a pointer to the node to be deleted. (the node to be deleted will not be the tail node)
this is what I have tried:-
public:
int value;
Node* next;
};
/*
headPtr is a reference to the head node(i.e. pointer to pointer) and
deleteNodePtr is the node which is to be deleted. You can see the Node definition above.
It is guaranteed that deleteNodePtr will not point to the last element.
*/
void deleteNode(Node** headPtr, Node* deleteNodePtr) {
Node* current;
current=*headPtr;
if (*headPtr==deleteNodePtr){
*headPtr=deleteNodePtr->next;
//delete current;
return;
}
else {
Node* prev = current;
while(current->next!=deleteNodePtr){
prev = current;
current=current->next;
}
prev->next =current->next;
//delete current;
return;
}
return;
}
I can see multiple things:
1 - In your while condition you are not checking that current is valid:
while(current->next!=deleteNodePtr){
prev = current;
current=current->next;
}
2- What probably is happening to you now: the deleteNodePtr points to the second element in the list, so you never enter to the while loop which means the prev == current which means that you are assigning current->next = current->next.
A solution for this would be:
void deleteNode(Node** headPtr, Node* deleteNodePtr) {
Node* current;
current = *headPtr;
if (current == deleteNodePtr) {
current = deleteNodePtr->next;
delete current;
}
else {
Node* prev = current;
current = current->next;
while (current!= nullptr && current != deleteNodePtr) {
prev = current;
current = current->next;
}
if(current!=nullptr)
{
prev->next = current->next;
delete current;
}
}
return;
}
3- You never checks if headPtr is valid, it couldn't be.
4- It's more a suggestion in the stylish thing rather than a code problem, so if you don't share this point of view, you can freely ignore it. At the very begin you assign *headPtr to current, but instead using current for further usage, you use headPtr. It would be much clear instead, if you always use current:
Original:
Node* current;
current=*headPtr;
if (*headPtr==deleteNodePtr){
*headPtr=deleteNodePtr->next;
//delete current;
return;
}
Suggestion:
Node* current;
current=*headPtr;
if (current==deleteNodePtr){
current=deleteNodePtr->next;
//delete current;
return;
}
This appears at first blush to be a C-style linked list written in C++. It's hard to say for sure since we don't have a bigger picture of your code. A C++ linked list would at least put these functions in a class, and not have a global Node.
The node is what's called an implementation detail. It helps us write the list, but users of the list class should not be able to declare their own nodes, nor should they be aware that nodes exist. Hopefully people using your class aren't calling this delete function explicitly.
Users of your List class might/should (depends) prefer to have iterators available to them. If anything writing iterators for your containers will allow them to be used with range-based for loops. Since your linked list appears to be singly linked, your iterators can only move forward. I also wrote my class with an iterator as I imagine it will help prevent copy/paste homework submissions in most cases.
Now to your function. Per my comment, passing the head as a double pointer makes no sense. Not once do you use it as a double pointer; you always de-reference it. So cut out the middle-man and pass it a node pointer from the get-go.
There's a case that's guaranteed not to happen, and that's that deleteNodePtr will never be the last node. That sounds bad. People can want to delete the last node, but they're allowed and no justification is given.
There's no code to catch an attempt to delete on an empty list.
Your specific issue seems to lie here:
while(current->next!=deleteNodePtr){
prev = current;
current=current->next;
}
You break out of the loop when current->next is the node to be deleted, and not current. You end up deleting the wrong node (current, which is 1 before deleteNodePtr), and that's likely going to cause issues down the line. For instance, if the second node is supposed to be deleted, you end up deleting your head, and that breaks stuff. I imagine that removing the ->next from your Boolean condition would fix the issue. I can't provide a deeper resolution without seeing more of your code.
=== Optional reading ===
Here's an extremely stripped down linked list written as a C++ class. You can run this code here: https://godbolt.org/z/7jnvje
Or compile it yourself.
#include <iostream>
// A simple linked list for integers
class List {
public:
List() = default;
~List();
// List(const List& other); // Copy ctor needed for Rule of 5
// List(List&& other) noexcept; // Move ctor needed for Rule of 5
void push_back(int val);
class iterator;
iterator begin();
iterator end();
iterator find(int val);
void erase(iterator it);
void clear();
// friend void swap(List& lhs, List& rhs); // Not Rule of 5; aids Rule of 5
// List& operator=(List other); // Assignment operator needed for Rule of 5
/*
* Quick note on Rule of 5 functions.
* If your class deals with heap-allocated resources, certain functions become
* required. The only one I included was the destructor. The signature of my
* assignment operator is different than you might see, but the reason is
* it's written to take advantage of the copy/swap idiom.
* https://stackoverflow.com/a/3279550/6119582
*/
private:
// Data
struct Node {
int value = 0;
Node *next = nullptr;
Node(int val) : value(val) {}
};
Node *m_head = nullptr;
Node *m_tail = nullptr;
// Functions
Node *find_node(int val);
};
class List::iterator {
public:
iterator(Node *loc) : location(loc) {}
iterator &operator++();
int operator*();
bool operator==(const List::iterator &rhs);
bool operator!=(const List::iterator &rhs);
private:
Node *location;
};
// List Implementation
List::~List() { clear(); }
void List::push_back(int val) {
if (m_tail) {
m_tail->next = new Node(val);
m_tail = m_tail->next;
return;
}
m_head = new Node(val);
m_tail = m_head;
return;
}
List::iterator List::begin() { return iterator(m_head); }
List::iterator List::end() { return iterator(nullptr); }
List::iterator List::find(int val) { return iterator(find_node(val)); }
void List::erase(iterator it) {
// Emtpy list or end()
if (!m_head || it == end())
return;
Node *toDelete = find_node(*it);
// Deleting head
if (toDelete == m_head) {
m_head = m_head->next;
delete toDelete;
return;
}
// Deleting tail
if (toDelete == m_tail) {
Node *walker = m_head;
while (walker->next != m_tail) {
walker = walker->next;
}
m_tail = walker;
delete m_tail->next;
m_tail->next = nullptr;
return;
}
// Delete any middle node; by moving value until it is the tail, then
// deleting the tail
while (toDelete->next) {
toDelete->value = toDelete->next->value;
if (toDelete->next == m_tail) {
m_tail = toDelete;
}
toDelete = toDelete->next;
}
delete toDelete;
m_tail->next = nullptr;
}
void List::clear() {
while (m_head) {
Node *tmp = m_head;
m_head = m_head->next;
delete tmp;
}
m_tail = nullptr;
}
List::Node *List::find_node(int val) {
if (!m_head) {
return nullptr;
}
Node *walker = m_head;
while (walker && walker->value != val) {
walker = walker->next;
}
return walker;
}
// List iterator implementation
List::iterator &List::iterator::operator++() {
location = location->next;
return *this;
}
int List::iterator::operator*() { return location->value; }
bool List::iterator::operator==(const List::iterator &rhs) {
return location == rhs.location;
}
bool List::iterator::operator!=(const List::iterator &rhs) {
return !(*this == rhs);
}
// Free function
// NOTE: Should take list by const reference, but I didn't add the necessary
// code for that. I'm not passing by value because I also left out Rule of 5
// code that is otherwise required.
// NOTE 2: Could also be templatized and made more generic to print any
// container, but that's outside the scope of this answer.
void print(List &list) {
for (auto i : list) {
std::cout << i << ' ';
}
std::cout << '\n';
}
int main() {
List list;
for (int i = 1; i <= 10; ++i) {
list.push_back(i);
}
print(list);
list.erase(list.find(1));
print(list);
list.erase(list.find(10));
print(list);
list.erase(list.find(6));
print(list);
auto it = list.begin();
for (int i = 0; i < 3; ++i) {
++it;
}
list.erase(it);
print(list);
list.erase(list.find(25)); // Bogus value; could throw if so desired
print(list);
}
Erasing is made much easier with a doubly-linked list, but we don't have one. My erase function makes some checks and handles the head and tail situations individually. For any node in the middle of the list, I don't bother deleting that node specifically. What I do instead is shuffle the value to be deleted to the tail of the list, and then delete the tail.
My comments indicate some things that were left out. I also didn't mark any functions as const. My iterator does not satisfy all requirements of a ForwardIterator. People can probably find other things I left out. I have a couple reasons for this. Mainly that this is quick and dirty code, and I prefer to not provide the temptation of a copy/paste solution.
It would be nice if all C++ instructors would actually teach C++, though. This form of linked list should not be taught in a C++ class anymore.

How to use templates in a c++ linked list?

I don't fully understand the concept of templates and am trying to get some help on how to implement one on my linked list below. I'm trying to get my code to be able to support the following types : List< List<std::string> > List<std::string> List<int>. I was wondering if there was any way someone could give me an example of how to convert these items into templates in addition to trying to explain what is happening? I'm new to c++ so any help I can get would be appreciated.
#include <string>
#include <iostream>
#include <cstddef>
using Item = std::string;
// TURN DList into a template!
class DList {
private:
class DListNode {
public:
Item item;
DListNode * next;
DListNode * prev;
DListNode(Item i, DListNode *n=nullptr, DListNode *p=nullptr) {
item = i;
next = n;
prev = p;
}
};
DListNode * head;
DListNode * tail;
public:
class iterator {
DListNode *node;
public:
iterator(DListNode *n = nullptr) {
node = n;
}
Item& getItem() { return node->item; }
void next() { node = node->next; }
void prev() { node = node->prev; }
bool end() { return node==nullptr; }
friend class DList;
};
public:
DList() {
// list is empty
head = nullptr;
tail = nullptr;
}
bool empty() {
return head==nullptr;
}
void append(Item a) {
DListNode *node = new DListNode(a,nullptr,tail);
if ( head == nullptr ) {
// empty list
head = node;
tail = node;
} else {
tail->next = node;
tail = node;
}
}
void insertAfter(iterator it, Item item)
{
if(head == nullptr || it.node == nullptr) { // NULL iterator means insert at head
DListNode *node = new DListNode(item,head); // next=head, prev=NULL
if ( head == nullptr) // same as zyBook
head = tail = node;
else { // if inserting before head, it.node==NULL
head->prev = node;
head = node;
}
} else if (it.node == tail) {
DListNode *node = new DListNode(item,nullptr,tail); // next=NULL, prev=old tail
tail->next = node;
tail = node;
} else {
DListNode *node = new DListNode(item,it.node->next,it.node);
it.node->next = node;
node->next->prev = node;
}
}
void erase (iterator it) {
DListNode *succ = it.node->next; // successor node
DListNode *pred = it.node->prev; // predecessor node
if (succ != NULL)
succ->prev = pred;
if (pred != NULL)
pred->next = succ;
if (it.node == head)
head = succ; // head is following node
if (it.node == tail)
tail = pred; // tail is previous node
delete it.node; // delete the node; not shown in zyBook, but necessary in C/C++
// iterator is now invalid, caller should not use it again
}
iterator begin() {
return iterator(head);
}
iterator reverse_begin() {
return iterator(tail);
}
};
template <typename Item>
std::ostream& operator << (std::ostream& out, DList<Item> &l)
{
out << "{";
auto it = l.begin();
out << it.getItem();
it.next();
for(; !it.end(); it.next())
{
out << ", " << it.getItem();
}
out << "}" << std::endl;
return out;
}
int main()
{
{
DList<std::string> l;
l.append("eggs");
l.append("milk");
l.append("bread");
std::cout << l;
}
{
DList<int> l;
l.append(1);
l.append(2);
l.append(3);
std::cout << l;
}
return 0;
}
Actually, you almost have all you need, but you are still using a regualar class with a concrete type.
using Item = std::string;
class DList { ... };
So first we drop the concrete type:
// using Item = std::string;
class DList { ... }; // sure Item is now undefined...
Then we tell the class to be a template
template <typename Item>
class DList { ... };
Now Item got re-introduced, but instead of being a concrete type, it's now a generic one. That's it, you have a template list (assuming the list is implemented correctly, I didn't check).
Whenever you now instantiate your list:
DList<int>;
DList<std::string>;
// ...
You create a totally new, independent data type (which means especially, that you cannot assign a DList<int> to a pointer to DList<double>, just all alike as you cannot assign a int to a pointer to double either).
When you instantiate a template, every occurence of a template parameter will be replaced with the type you instantiated the template with, e. g. in DList<int>, every occurence of Item will be replaced with int.
Well, all this is just a very short introduction, there's quite a lot to follow yet, but that's rather to be handled in book than in an answer on stackoverflow...
Some notes to your node's constructor, though:
DListNode(Item i /* , ... */) { item = i; }
At very first, you should get used to using constructor's initialiser list (not to be confused with std::initializer_list):
DListNode(Item i /* , ... */) : item(i) { }
You avoid default initiasation + assignment in favour of direct initialisation by value. Additionally, some types (non-default constructible ones, const members and references) only can be initialised that way.
Then you are producing an unnecessary copy:
DListNode(Item i /* , ... */) : item(i) { }
// ^ temporary copy ^ final copy, created from temporary
You avoid that copy, if you accept the item by reference:
DListNode(Item const& i /* , ... */) : item(i) { }
// now copies from reference, one copy less
You can additionally provide move semantics:
DListNode(Item&& i /* , ... */) : item(std::move(i)) { }
so that objects you don't need outside the list any more can be moved into (well, actually their contents). In some cases, this can be much cheaper than a full copy...
All said about the constructor (apart from the initialiser list) applies to the append and insertAfter functions as well.
Initialiser lists and avoiding copies is general advice, unrelated to templates...

C++ Linked List Assignment Operator Problem

I am writing an Ordered Linked List class definition (OLList). I have written the assignment operator function, but when I try to test it by chaining assignment operations, the program gets caught in the while loop of the OLList::copy function. I know this because I tested using console prints.
//OLList.h
struct Node {
ListItem item;
Node *next;
};
class OLList {
public:
OLList& OLList::operator =(const OLList& rhs)
{
if (this != &rhs) {
destroy();
copy(rhs);
}
return *this;
}
void OLList::destroy()
{
Node *current_node = this->headM;
Node *next_node;
while(current_node->next != nullptr)
{
next_node = current_node->next;
delete(current_node);
current_node = next_node;
}
return;
}
void OLList::copy(const OLList& source)
{
Node *new_node, *current_node;
Node *current_source_node = source.headM;
this->headM->item = source.headM->item;
current_node = this->headM;
while(current_source_node->next != nullptr)
{
new_node = new(Node);
current_node->next = new_node;
current_node = current_node->next;
current_source_node = current_source_node->next;
current_node->item = current_source_node->item;
}
return;
}
}
Below is the code used to test the class. I have made sure that the print() function works fine so that's definitely not an issue.
//main.cpp
int main()
{
OLList the_list;
the_list.insert(1);
the_list.insert(2);
OLList second_list;
second_list.insert(3);
second_list.insert(4);
OLList third_list;
third_list.insert(5);
third_list.insert(6);
third_list = second_list = the_list;
third_list.print();
}
When it is compiled and run, the program never terminates as it is caught in the loop mentioned above.
Your destroy() method will fail if headM is nullptr. You should be using while(current_node != nullptr) instead of while(current_node->next != nullptr). But more importantly, it doesn't reset headM to nullptr after destroying the list. So after operator= calls destroy(), headM is no longer in a valid state for copy() to use.
Your copy() method is similarly not checking if either source or target headM are nullptr. But more importantly, it assumes the target list is empty beforehand, otherwise it leaks memory, if it does not crash outright (per above). And frankly, it simply is not coded correctly in general to copy one list to another.
So, your code is invoking undefined behavior, this anything could happen.
Like #PaulMcKenzie stated in comments, you really should be using a proper copy constructor instead (and a destructor - and since you are clearly using C++11 or later, a move constructor and move assignment operator, too - see the Rule of 5). Your assignment operator can then be implemented using your copy constructor (and likewise for move assignment).
Try something more like this:
struct Node {
ListItem item;
Node *next = nullptr;
Node(const ListItem &value) : item(value) {}
};
class OLList {
private:
Node *headM = nullptr;
public:
OLList() = default;
OLList(const OLList &src)
{
Node *current_source_node = src.headM;
Node **current_node = &headM;
while (current_source_node)
{
*current_node = new Node(current_source_node->item);
current_node = &((*current_node)->next);
current_source_node = current_source_node->next;
}
/* alternatively:
Node *current_source_node = src.headM;
while (current_source_node) {
insert(current_source_node->item);
}
*/
}
OLList(OLList&& src)
{
src.swap(*this);
}
~OLList()
{
Node *next_node;
while (headM)
{
next_node = headM->next;
delete headM;
headM = next_node;
}
}
void clear() {
OLList().swap(*this);
}
OLList& operator=(const OLList& rhs)
{
if (this != &rhs) {
OLList(rhs).swap(*this);
}
return *this;
}
OLList& OLList::operator=(OLList&& rhs)
{
OLList(std::move(rhs)).swap(*this);
return *this;
}
void swap(OLList &other) {
std::swap(headM, other.headM);
}
void insert(const ListItem &value) {
...
}
void print() const {
...
}
...
};

Making a copy constructor more flexible for ADT queue

I have constructed a copy constructor for an ADT queue. The copy constructor works fine. I would want to improve my code, but I don't really know how to shorten it to make it more flexible. Code is given below:
template <typename T>
Queue <T>::Queue(const Queue & other)
{
if (other.first == nullptr)
{
first = nullptr;
nrOfElements = 0;
}
else
{
Node* saveFirst;
Node* walker;
first = other.first;
walker = new Node(first->data);
saveFirst = walker;
while (first->next != nullptr)
{
walker->next = new Node(first->next->data);
walker = walker->next;
first = first->next;
}
walker->next = nullptr;
first = saveFirst;
}
this->nrOfElements = other.nrOfElements;
}
The class Queue also contains an inner private Node class which contains the pointers first, next, etc:
private:
int nrOfElements;
class Node
{
public:
Node* next;
T data;
Node(T data)
{
this->data = data;
}
};
Node* first;
So, I would appreciate any suggestions/examples of how the copy constructor code above could be improved, as I'm a bit lost on the task.
try this one
may be in easier than your code
template <typename T>
Queue <T>::Queue(const Queue & other)
{
Front =NULL;Rear=NULL;Count=0;
Node *p= other.Front;
while(p!=NULL){
EnQueue(p->GetData());
Rear->SetData(p->GetData());
p=p->next;
}
}
First we set NULL value to Front & Rear and 0 for Count second make new Node to get the data from every Node in old Queue and copy it to the new Queue.

Copy constructor for a binary tree C++

I have a Tree class with the following definition:
class Tree {
Tree();
private:
TreeNode *rootPtr;
}
TreeNode represents a node and has data, leftPtr and rightPtr.
How do I create a copy of a tree object using a copy constructor? I want to do something like:
Tree obj1;
//insert nodes
Tree obj2(obj1); //without modifying obj1.
Any help is appreciated!
Pseudo-code:
struct Tree {
Tree(Tree const& other) {
for (each in other) {
insert(each);
}
}
void insert(T item);
};
Concrete example (changing how you walk the tree is important to know, but detracts from showing how the copy ctor works, and might be doing too much of someone's homework here):
#include <algorithm>
#include <iostream>
#include <vector>
template<class Type>
struct TreeNode {
Type data;
TreeNode* left;
TreeNode* right;
explicit
TreeNode(Type const& value=Type()) : data(value), left(0), right(0) {}
};
template<class Type>
struct Tree {
typedef TreeNode<Type> Node;
Tree() : root(0) {}
Tree(Tree const& other) : root(0) {
std::vector<Node const*> remaining;
Node const* cur = other.root;
while (cur) {
insert(cur->data);
if (cur->right) {
remaining.push_back(cur->right);
}
if (cur->left) {
cur = cur->left;
}
else if (remaining.empty()) {
break;
}
else {
cur = remaining.back();
remaining.pop_back();
}
}
}
~Tree() {
std::vector<Node*> remaining;
Node* cur = root;
while (cur) {
Node* left = cur->left;
if (cur->right) {
remaining.push_back(cur->right);
}
delete cur;
if (left) {
cur = left;
}
else if (remaining.empty()) {
break;
}
else {
cur = remaining.back();
remaining.pop_back();
}
}
}
void insert(Type const& value) {
// sub-optimal insert
Node* new_root = new Node(value);
new_root->left = root;
root = new_root;
}
// easier to include simple op= than either disallow it
// or be wrong by using the compiler-supplied one
void swap(Tree& other) { std::swap(root, other.root); }
Tree& operator=(Tree copy) { swap(copy); return *this; }
friend
ostream& operator<<(ostream& s, Tree const& t) {
std::vector<Node const*> remaining;
Node const* cur = t.root;
while (cur) {
s << cur->data << ' ';
if (cur->right) {
remaining.push_back(cur->right);
}
if (cur->left) {
cur = cur->left;
}
else if (remaining.empty()) {
break;
}
else {
cur = remaining.back();
remaining.pop_back();
}
}
return s;
}
private:
Node* root;
};
int main() {
using namespace std;
Tree<int> a;
a.insert(5);
a.insert(28);
a.insert(3);
a.insert(42);
cout << a << '\n';
Tree<int> b (a);
cout << b << '\n';
return 0;
}
It depends on whether you want a shallow or deep copy. Assuming a deep copy, you need to be able to copy whatever's at the "leaves" hanging off a TreeNode object; so ideally the functionality should be in TreeNode (unless Tree is a friend class of TreeNode that you've designed to be deeply familiar with its implementation, which is often the case of course;-). Assuming something like...:
template <class Leaf>
class TreeNode {
private:
bool isLeaf;
Leaf* leafValue;
TreeNode *leftPtr, *rightPtr;
TreeNode(const&Leaf leafValue);
TreeNode(const TreeNode *left, const TreeNode *right);
...
then you could add to it a
public:
TreeNode<Leaf>* clone() const {
if (isLeaf) return new TreeNode<Leaf>(*leafValue);
return new TreeNode<Leaf>(
leftPtr? leftPtr->clone() : NULL,
rightPtr? rightPtr->clone() : NULL,
);
}
If Tree is taking care of this level of functionality (as a friend class), then obviously you'll have the exact equivalent but with the node being cloned as an explicit arg.
Two basic options:
If you have an iterator available, you can simply iterate over the elements in the tree and insert each one manually, as R. Pate described. If your tree class doesn't take explicit measures to balance the tree (e.g. AVL or red-black rotations), you'll end up effectively with a linked list of nodes this way (that is, all the left child pointers will be null). If you are balancing your tree, you'll effectively do the balancing work twice (since you already had to figure it out on the source tree from which you're copying).
A quicker but messier and more error-prone solution would be to build the copy top down by doing a breadth-first or depth-first traversal of the source tree structure. You wouldn't need any balancing rotations and you'd end up with an identical node topology.
Here's another example I used with a binary tree.
In this example, node and tree are defined in separate classes and a copyHelper recursive function helps the copyTree function. The code isn't complete, I tried to put only what was necessary to understand how the functions are implemented.
copyHelper:
void copyHelper( BinTreeNode<T>* copy, BinTreeNode<T>* originalNode ) {
if (originalTree == NULL)
copy = NULL;
else {
// set value of copy to that of originalTree
copy->setValue( originalTree->getValue() );
if ( originalTree->hasLeft() ) {
// call the copyHelper function on a newly created left child and set the pointers
// accordingly, I did this using an 'addLeftChild( node, value )' function, which creates
// a new node in memory, sets the left, right child, and returns that node. Notice
// I call the addLeftChild function within the recursive call to copyHelper.
copyHelper(addLeftChild( copy, originalTree->getValue()), originalTree->getLeftChild());
}
if ( originalTree->hasRight() ) { // same with left child
copyHelper(addRightChild(copy, originalTree->getValue()), originalTree->getRightChild());
}
} // end else
} // end copyHelper
copy: returns a pointer to the new tree
Tree* copy( Tree* old ) {
Tree* tree = new Tree();
copyHelper( tree->root, oldTree->getRoot() );
// we just created a newly allocated tree copy of oldTree!
return tree;
} // end copy
Usage:
Tree obj2 = obj2->copy(obj1);
I hope this helps someone.
When your class has a pointer pointing to dynamically allocated memory, in the copy constructor of that class you need to allocate memory for newly created object. Then you need to initialize newly allocated memory with whatever the other pointer pointing at. Here is an example how you need to deal with a class having dynamically allocated memory:
class A
{
int *a;
public:
A(): a(new int) {*a = 0;}
A(const A& obj): a(new int)
{
*a = *(obj.a);
}
~A() {delete a;}
int get() const {return *a;}
void set(int x) {*a = x;}
};
You can try something like (untested)
class Tree {
TreeNode *rootPtr;
TreeNode* makeTree(Treenode*);
TreeNode* newNode(TreeNode* p)
{
TreeNode* node = new Treenode ;
node->data = p->data ;
node->left = 0 ;
node->right = 0 ;
}
public:
Tree(){}
Tree(const Tree& other)
{
rootPtr = makeTree(other.rootPtr) ;
}
~Tree(){//delete nodes}
};
TreeNode* Tree::makeTree(Treenode *p)
{
if( !p )
{
TreeNode* pBase = newNode(p); //create a new node with same data as p
pBase->left = makeTree(p->left->data);
pBase->right = makeTree(p->right->data);
return pBase ;
}
return 0 ;
}