Binary search tree destructor: free() invalid pointer - c++

For my current programming assignment I have to construct a binary search tree and use it to insert words from a file in alphabetical order, to make a concordance list. The program is complete and outputs correctly; the main bulk of the program works perfectly. But when the program finishes and destructors are called, it crashes with error in ./concordancebst : free(): invalid pointer. I'm at a loss as to what the problem is, my destructor looks like it would work and looking at code online it seems as though it should work as well. I could probably get around this by using a std::unique_ptr but it seems a bit overkill in the sense of the scope of the program (also overkill in the sense that we haven't covered smart pointers, everything we have done in the class involving dynamic memory has been handled through manual allocating/deallocating).
BST.h
#ifndef BST_H
#define BST_H
#include <string>
class BST {
public:
BST() { root = nullptr; }
~BST();
void insert(const std::string word);
int getCount(const std::string word);
int length();
friend std::ostream& operator << (std::ostream &out_s, BST b);
private:
struct Node {
std::string word;
int count;
Node *left;
Node *right;
Node() { word = ""; count = 0; left = nullptr; right = nullptr;}
~Node();
};
Node *root;
void RInsert(Node* &t, std::string word);
int countNodes(Node *p);
void print(Node *p, std::ostream &out_s);
};
#endif
BST.cpp
#include "BST.h"
#include <string>
#include <iostream>
#include <iomanip>
BST::Node::~Node() {
delete left;
delete right;
}
BST::~BST() {
delete root;
}
int BST::countNodes(Node *p) {
if(p == nullptr)
return 0;
else
return countNodes(p->left) + countNodes(p->right) + 1;
}
int BST::getCount(const std::string word) {
Node *p = root;
while(true) {
if(p == nullptr)
return 0;
else if(word.compare(p->word) < 0)
p = p->left;
else if(word.compare(p->word) == 0)
return p->count;
else
p = p->right;
}
}
int BST::length() {
return countNodes(root);
}
void BST::RInsert(Node* &t, std::string word) {
if(t == nullptr) {
t = new Node;
t->word = word;
t->left = nullptr;
t->right = nullptr;
t->count++;
}
else if(word.compare(t->word) == 0)
t->count++;
else if(word.compare(t->word) < 0)
RInsert(t->left, word);
else //word.compare(t->word > 0)
RInsert(t->right, word);
}
void BST::insert(const std::string word) {
RInsert(root, word);
}
void BST::print(Node *p, std::ostream &out_s) {
if(p != nullptr) {
print(p->left, out_s);
out_s << std::setw(13) << p->word << std::setw(10) << p->count << std::endl;
print(p->right, out_s);
}
}
std::ostream &operator << (std::ostream &out_s, BST b) {
out_s << std::setw(13) << "Word" << std::setw(10) << "Count" << std::endl;
out_s << "---------------------------------------------" << std::endl;
b.print(b.root, out_s);
out_s << "---------------------------------------------" << std::endl;
out_s << "The file contains " << b.length() << " distinct words." << std::endl;
return out_s;
}

std::ostream &operator << (std::ostream &out_s, BST b)
Pass by value^. b is copied.
Since BST has no copy constructor, the default copy constructor is invoked and does not perform a deep copy. b contains copies of the source BST's pointers, and when b is destroyed on return from the function, it takes all of the source's Nodes with it to the grave.
Fixes are twofold:
Most directly, pass by reference.
std::ostream &operator << (std::ostream &out_s, BST & b)
Indirectly, this code violates the Rule of Three. BST and BST::Node need copy constructors and assignment operators to be used safely.
Edit
A minimal test case would be something like:
#include <iostream>
#include "BST.h"
int main()
{
BST t;
t.insert("A");
std::cout << t << std::endl;
return 0;
}

Related

Destructors called without matching constructor

I am trying to figure out how C++ resource management works, especially in relation to constructors/destructors. This is my test code;
ylist.h
#pragma once
template <class T>
struct ListNode
{
T elem;
struct ListNode* next;
ListNode(T elem):elem(elem),next(nullptr) {}
};
template <class T>
class List
{
ListNode<T> *head;
public:
List() : head(nullptr)
{
}
~List()
{
ListNode<T>* cursor = head;
while (cursor != nullptr)
{
ListNode<T>* next = cursor->next;
delete cursor;
cursor = next;
}
}
void append(T item)
{
ListNode<T>* n = new ListNode<T>(item);
if (head == nullptr)
{
head = n;
}
else
{
ListNode<T>* cursor = head;
while (cursor->next != nullptr)
cursor = cursor->next;
cursor->next = n;
}
}
};
main.cpp
#include <iostream>
#include "ylist.h"
using namespace std;
class myObj
{
int val;
public:
myObj(int val):val(val)
{
cout << "myObj#" << this << "(" << val << ")" << endl;
}
~myObj()
{
cout << "~myObj#" << this << "(" << val << ")" << endl;
}
};
int main()
{
List<myObj> myList;
for (int i = 0; i < 3; i++)
{
myList.append(myObj(i));
}
}
And here is the output;
myObj#00000039614FFAC0(0)
~myObj#00000039614FFAD0(0)
~myObj#00000039614FFAC8(0)
~myObj#00000039614FFAC0(0)
myObj#00000039614FFAC0(1)
~myObj#00000039614FFAD0(1)
~myObj#00000039614FFAC8(1)
~myObj#00000039614FFAC0(1)
myObj#00000039614FFAC0(2)
~myObj#00000039614FFAD0(2)
~myObj#00000039614FFAC8(2)
~myObj#00000039614FFAC0(2)
~myObj#0000019878DF6100(0)
~myObj#0000019878DF5F20(1)
~myObj#0000019878DF6200(2)
According to above output, constructor called for each object once, but destructors called four times.
I was expecting to see multiple temporary copies being constructed/destructed but why is number of constructors/destructos is not matching.
If, for example, I was opening files or db connections on constructors and closing them on destructors, would I get in trouble in above scenario?
Add this copy constructor to your myObj function in order to see how the additional myObj objects are being constructed. Once you have this, you'll see that the number of constructor-calls matches the number of destructor-calls.
myObj(const myObj & rhs):val(rhs.val)
{
cout << "COPY CTOR myObj#" << this << "(" << val << ")" << endl;
}

How to access array elements of pointers

How can I access elements from my Node pointer array? The cout at the bottom returns an address "0xb7738ff4". Even if I make a constructor and set each element to NULL, I cannot modify any of them later on.
#include <iostream>
using namespace std;
class Node
{
public:
char character;
};
class Tree
{
public:
Node* nodes[26];
};
int main() {
Tree t;
//t.nodes[0]->character = 'a';
cout << "\"" << (t.nodes[0]) << "\"";
}
http://ideone.com/XvLme9
t.nodes[0] is returning a Node* pointer. You are passing that pointer as-is to cout, that is why it is printing a memory address (and a random one at that, because you are not initializing the array). If you want to print the character of a node, you have to dereference the Node* pointer, exactly like your commented out code is doing (which is the correct way to do it):
t.nodes[0]->character
You just have to make sure that nodes[0] returns a valid pointer to a real Node object to begin with:
Tree t;
t.nodes[0] = new Node; // <-- here
t.nodes[0]->character = 'a';
std::cout << "\"" << t.nodes[0]->character << "\"" << std::endl;
Don't forget to delete the node when you are done using it. Tree should have a destructor that frees the nodes it owns.
Try something more like this:
#include <iostream>
#include <stdexcept>
class Node
{
public:
char character;
Node(char c = 0);
};
class Tree
{
private:
Node* nodes[26];
int count;
public:
Tree();
~Tree();
Node* add(char c);
Node* get(int idx);
};
Node::Node(char c)
: character(c)
{
}
Tree::Tree()
: count(0)
{
for (int i = 0; i < 26; ++i)
nodes[i] = NULL;
}
Tree::~Tree()
{
for (int i = 0; i < count; ++i)
delete nodes[i];
}
Node* Tree::add(char c)
{
if (count == 26)
throw std::runtime_error("nodes array is at its max capacity");
Node *node = new Node(c);
nodes[count++] = node;
return node;
}
Node* Tree::get(int idx)
{
if ((idx < 0) || (idx >= count))
throw std::out_of_range("invalid index");
return nodes[idx];
}
int main()
{
Tree t;
t.add('a');
std::cout << "\"" << t.get(0)->character << "\"" << std::endl;
}
With that said, you should use std::list or std::forward_list instead of writing your own tree class:
#include <list>
int main()
{
std::list<char> l;
l.push_back('a');
std::cout << "\"" << l.front() << "\"" << std::endl;
}
Or:
#include <list>
class Node
{
public:
char character;
// other things here...
Node(char c = 0);
Node(const Node &src);
Node& operator=(const Node &rhs);
};
Node::Node(char c)
: character(c)
{
}
Node::Node(const Node &src)
: character(src.character)
{
}
Node& Node::operator=(const Node &rhs)
{
character = src.character;
return *this;
}
int main()
{
std::list<Node> l;
l.push_back('a');
std::cout << "\"" << l.front().character << "\"" << std::endl;
}

Binary Search Tree Display issue

I have a lab assignment that I have been working on for the last couple of weeks and I am stuck and desperately need help as this will be about 50% of the final project.
The assignment is to create a Binary Search Tree in C++. We have to read in the words from the Declaration of Independence into a Binary Search Tree. I have my search and insert methods "working properly" meaning that they aren't throwing any errors. The problem I am having is displaying the BST to figure out if everything is working properly. The display method should be called by the overloaded operator >> to display the tree.
The errors I keep getting are:
"error C3867: 'BST::display': function call missing
argument list; use '&BST::display' to create a pointer to
member"
and the other one is
"error C2679: binary '<<' : no operator found which takes a right-hand
operand of type 'overloaded-function' (or there is no acceptable
conversion)."
Last time I rebuilt the program it shows "ItelliSense: a pointer to a bound function may only be used to call the function."
#include "stdafx.h"
#include <string>
#include <iostream>
#include <fstream>
using namespace std;
template <typename T>
class BST{
private:
struct node {
T data;
struct node* left;
struct node* right;
};
node* root;
public:
BST()
{
root = NULL;
}
bool isEmpty() const { return root == NULL; }
~BST();
template <typename T>
void insert(T d)
{
node* t = new node;
node* parent;
t->data = d;
t->left = NULL;
t->right = NULL;
parent = NULL;
if (isEmpty()) root = t;
else {
node* current;
current = root;
while (current)
{
parent = current;
if (t->data > current->data) current = current->right;
else current = current->left;
}
if (t->data < parent->data)
parent->left = t;
else
parent->right = t;
}
}
template<typename T>
bool search(T d)
{
if (root == NULL)
return false;
else if (d == root->item) {
return true;
}
else if (d < root->item) {
return search(root->left, d);
}
else {
return search(root->right, d);
}
}
template<typename T>
void display(node *p, std::ostream& os)
{
if (p != NULL)
{
if (p->left) display(p->left);
os << " " << p->data << " ";
if (p->right) display(p->right);
}
}
template<typename T> friend std::ostream& operator<<(std::ostream& os, const BST<T>& obj)
{
obj.display(os, obj.root);
}
};
int main( )
{
BST<string> s;
ifstream inFile;
string word, tmp;
string filename = "Independence.txt";
ifstream fstr(filename.c_str());
while (inFile >> word) {
s.insert(word);
}
inFile.close();
cout << s << std::endl;
cout << "Search for: ";
cin.ignore(1);
cin >> tmp;
s.search(tmp);
return 0;
};
template<typename T>
void display(node *p)
This takes a parameter with a node pointer.
Then you call it later with:
BST<string> s;
cout << s.display << endl;
But you don't actually pass it any parameters here. The compiler then complains it can't figure out how to call that function. Because that function has a return type of void it can't figure out how to print it as you aren't actually returning anything for cout to print. You will want to fix both these problems before you move on, given that you said it's an assignment I'll leave you to figure out how to do that :).
There seem to be a number of problems with your code. I suspect display and search should not be separately templated - this would actually be a template member function inside a template class and I don't think that's what you intended. Also, the search function refers to node::item, but the declaration of the node type has node::data. Finally, BST::display is written to be a void function taking a node in a way that it could be declared static but your usage is as if you expect it to work like a member function. It doesn't return anything so it certainly can't be passed to iostream::operator<<. Better would be to have display take the iostream as input and then call it as either root->display(cout) or display(cout, root) depending on whether you want it to be a member function or a static function.
you have a couple of concept errors,
the display method returns void so you cannot pass it to cout that expect something to show.
So, the easy change for you will be to add a new method called display_tree like this
void display_tree()
{
display(root);
}
and in your main only call the method
s.display_tree();
not like this
cout << s.display << std::endl; //this is wrong cause this is not even calling a method
another option is to override the << operator, but editing your diplay method like this
template<typename T> void display(node *p, std::ostream& os)
{
if (p != NULL)
{
if (p->left) display(p->left);
os << " " << p->data << " ";
if (p->right) display(p->right);
}
}
and then outside your class
template<typename T> friend std::ostream& operator<<(std::ostream& os, const BST<T>& obj)
{
obj.display(obj.root);
}
and call it in your programa like this
cout << s << std::endl;
in order to this to work, the T needs to have the operator << overloaded (which is the case of the string in your case)
try with this instead -- new version compiling
void display(node *p, std::ostream& os) const
{
if (p != NULL)
{
if (p->left) display(p->left,os);
os << " " << p->data << " ";
if (p->right) display(p->right,os);
}
}
template<typename T> friend std::ostream& operator<<(std::ostream& os, const BST<T>& obj)
{
obj.display(obj.root,os);
return os;
}
Here is your code compiling and fixed the search
#include "stdafx.h"
#include <string>
#include <iostream>
#include <fstream>
using namespace std;
template <typename T>
class BST{
private:
struct node {
T data;
struct node* left;
struct node* right;
};
node* root;
template<typename T> bool search(node* p, T d)
{
if (!p) {
return false;
}
if (d == p->data) {
return true;
}
else if (d < p->data) {
return search(root->left, d);
}
else {
return search(root->right, d);
}
}
public:
BST()
{
root = NULL;
}
bool isEmpty() const { return root == NULL; }
~BST();
template <typename T>
void insert(T d)
{
node* t = new node;
node* parent;
t->data = d;
t->left = NULL;
t->right = NULL;
parent = NULL;
if (isEmpty()) root = t;
else {
node* current;
current = root;
while (current)
{
parent = current;
if (t->data > current->data) current = current->right;
else current = current->left;
}
if (t->data < parent->data)
parent->left = t;
else
parent->right = t;
}
}
template<typename T> bool search(T d)
{
if (root == NULL)
return false;
else if (d == root->data) {
return true;
}
else if (d < root->data) {
return search(root->left, d);
}
else {
return search(root->right, d);
}
}
void display(node *p, std::ostream& os) const
{
if (p != NULL)
{
if (p->left) display(p->left,os);
os << " " << p->data << " ";
if (p->right) display(p->right,os);
}
}
template<typename T> friend std::ostream& operator<<(std::ostream& os, const BST<T>& obj)
{
obj.display(obj.root,os);
return os;
}
};
int main()
{
BST<string> s;
ifstream inFile;
string word, tmp;
string filename = "Independence.txt";
ifstream fstr(filename.c_str());
while (inFile >> word) {
s.insert(word);
}
inFile.close();
cout << s << std::endl;
cout << "Search for: ";
cin.ignore(1);
cin >> tmp;
s.search(tmp);
return 0;
};

Postincrementation operator in linked list

I had to write a program that handle this main code:(not allowed to change it)
list<int> iv;
iv["john"] = 23;
int ia = iv["john"]++;
int ib = iv["john"];
cout << ia << " " << ib << endl; // prints 23 24
try{
cout << iv["jack"] << endl; // should throw an exception
}catch(list<int>::Uninitialized&)
{
cout << "Uninitialized map element!" << endl;
};
Here is my code:
#ifndef EXAM_H
#define EXAM_H
#include <iostream>
#include <string>
using namespace std;
template <class TYPE>
class list
{
private:
struct node
{
TYPE value;
string index;
bool isInit;
node *next;
};
node *head;
node *current;
public:
class Cref
{
friend class list;
list& s;
string position;
Cref (list& ss, string pos): s(ss), position(pos) {};
public:
operator TYPE() const
{
return s.read(position);
}
Cref& operator = (TYPE val)
{
s.write(position,val);
return *this;
};
Cref& operator = (const Cref& ref)
{
return operator= ((TYPE)ref);
};
};
class Uninitialized{};
list ()
{
cout << "constructor\n";
head = NULL;
current = NULL;
}
~list ()
{
while (head)
{
node *t = head->next;
delete head;
head = t;
};
}
TYPE read (string ind) const
{
cout << "read\n";
node *t = head;
while(t)
{
if(t->index == ind && t->isInit == true) return t->value;
else t = t->next;
}
throw Uninitialized();
}
void write (string ind, TYPE value_)
{
cout << "write\n";
node *t = new node;
t->next = head;
head = t;
head->value = value_;
head->index = ind;
head->isInit = true;
}
TYPE operator[] (string ind) const
{
cout << "read\n";
node *t = head;
while(t)
{
if(t->index == ind && t->isInit == true) return t->value;
else t = t->next;
}
throw Uninitialized();
}
Cref operator[] (string ind)
{
return Cref(*this, ind);
}
};
#endif
Everything works great, but only when I comment out postincrementation operation in main program
int ia = iv["john"]++;
As you can see I have a struct node where I put all variables and I want to increment value by one in node where the key is "john". Is there any way to implement operator++ for this code ?
I am not allowed to use std::map.
The usual approach to your problem is defining the array subscript operators as
const TYPE& operator[](string ind) const;
TYPE& operator[](string ind);
In this way, you do not have to bother a single bit about the operator++: Since iv["John"] returns a reference to int, iv["John"]++ will call the int post-increment operator which is built-in.
Yes, I have already tried this solution, but compiler do not distinguish between reading and writing and still using non-const version. So I had to build proxy class Cref that helps to distinguish.
I have also already find a solution to operator++ problem.
This operation had to be from Cref level. I created
Cref& operator++ (int val)
{
s.increment(position,val);
return *this;
};
And increment function in main class body as follows:
void increment (string ind, int value_)
{
cout << "increment\n";
node *t = head;
while(t)
{
if(t->index == ind && t->isInit == true) t->value = t->value + 1;
t = t->next;
}
}
That fully solved my problem.

Linked list problem with constructor and destructor

Hi
I have some issue regarding constructor and destructor. I have list class, which has two inner classes, one private class for the list nodes, and one public iterator class.
Now for the issue, I have written a non-member print function which uses the inner iterator class. When i use this non-member function it will end calling the destructor for the iterator. It doesn't end here though because for some reason it will also call for the list class's destructor. Which causes some problem when I want to print the list content again.
I don't understand why it call the list class destructor as well and wonder if someone kindly can tell me that, and how I should fix it.
I have attached all the code related to the problem
Main
#include <iostream>
#include "sorted_list.h"
#include "iterator.h"
using namespace std;
void list_print(ostream& os, sorted_list list)
{
sorted_list::iteratorn it(&list);
while( ! it.iterator_end())
{
os << "key = " << setw(3) << it.iterator_get_key() << ", "
<< "value = " << setw(5) << it.iterator_get_value() << endl;
it.iterator_next();
}
os << endl;
}
int main()
{
sorted_list a;
a.insert(4,4);
a.insert(5,5);
list_print(cout,a);
list_print(cout,a);
}
sorted_list.cc
#include "sorted_list.h"
sorted_list::sorted_list()
{
cout << "construct sorted_list" << endl;
this->first = 0;
}
sorted_list::~sorted_list()
{
cout << "destruct sorted_list" << endl;
destroy(this->first);
}
void sorted_list::destroy(list_link* item)
{
cout << "destroy list_link" << endl;
if(item)
{
destroy(item->next);
delete item;
}
}
void sorted_list::insert(int key, double value)
{
list_link *curr;
list_link *prev = 0;
curr = first;
while(curr)
{
if(value < curr->value)
break;
prev = curr;
curr = curr->next;
}
if(this->first == 0 || prev == 0) //if empty or add first
{
//first = create(key, value, this->first);
first = new list_link(key, value, this->first);
}
else if(curr == 0)
{
//prev->next = create(key, value, 0);
prev->next = new list_link(key, value, 0);
}
else
{
//prev->next = create(key, value, curr);
prev->next = new list_link(key, value, curr);
}
}
void sorted_list::remove(my_key_type key)
{
list_link *curr = first;;
list_link *prev = 0;
while(curr)
{
if(curr->key == key)
{
list_link *remove;
if(prev == 0)
{
first = curr->next;
delete curr;
curr = first;
}
else
{
remove = curr;
curr = curr->next;
prev->next = curr;
delete remove;
}
continue;
}
prev = curr;
curr = curr->next;
}
}
sorted_list::list_link* sorted_list::clone(list_link* item)
{
list_link* copyItem= new list_link(item->key,item->value,0);
if(item->next!= 0)
copyItem->next=clone(item->next);
return copyItem;
// ADD YOUR CODE HERE ( 4 well formatted lines in reference solution )
}
void sorted_list::copy(sorted_list* my_this_destination)
{
if (my_this_destination->first == 0) // copy if empty
{
cout << "Copy" << endl;
//list_destroy(my_this_destination);
my_this_destination->first = clone(first);
}
}
double sorted_list::find(int key)
{
list_link *travel = this->first;
while(travel)
{
cout << travel->key << "==" << key << endl;
if(travel->key == key)
return travel->key;
travel = travel->next;
}
return -1;
}
int sorted_list::size()
{
list_link *travel = this->first;
int i = 0;
while( travel )
{
travel = travel->next;
i++;
}
return i;
}
sorted_list.h
#ifndef _SORTED_LIST_H_
#define _SORTED_LIST_H_
#include <iostream>
#include <iomanip>
using namespace std;
typedef int my_key_type;
typedef double my_value_type;
class sorted_list
{
public:
sorted_list();
~sorted_list();
void insert(int key, double value);
void remove(my_key_type key);
void copy(sorted_list* my_this_destination);
void destroy();
void init(struct my_list* my_this);
void print();
void print2();
double find(int key);
int size();
private:
class list_link // An inner class inside sorted_list
{
public:
list_link (my_key_type key, my_value_type value, list_link* next = 0);
~list_link();
my_key_type key;
my_value_type value;
list_link *next;
};
list_link* first;
list_link* clone(list_link* item);
void destroy(list_link* item);
// More declarations
public:
class iteratorn
{
public:
iteratorn();
~iteratorn();
iteratorn(sorted_list *item);
list_link* list_begin();
bool iterator_end();
void iterator_next();
int iterator_get_key();
double iterator_get_value();
private:
sorted_list::list_link* current;
};
};
#endif
iteratorn.cc
#include "iterator.h"
#include "sorted_list.h"
sorted_list::iteratorn::iteratorn()
{
}
sorted_list::iteratorn::iteratorn(sorted_list *list)
{
cout << "construct iteratorn" << endl;
this->current = list->first;
}
sorted_list::iteratorn::~iteratorn()
{
cout << "destruct iteratorn" << endl;
}
sorted_list::list_link* sorted_list::iteratorn::list_begin()
{
return current;
}
void sorted_list::iteratorn::iterator_next()
{
current = current->next;
}
int sorted_list::iteratorn::iterator_get_key()
{
return current->key;
}
double sorted_list::iteratorn::iterator_get_value()
{
return current->value;
}
list_link.cc
#include "sorted_list.h"
sorted_list::list_link::list_link(my_key_type key, my_value_type value, list_link* next)
{
this->key = key;
this->value = value;
this->next = next;
}
sorted_list::list_link::~list_link()
{
cout << "list_link destructor" << endl;
}
Your function void list_print(ostream& os, sorted_list list) takes a sorted_list parameter by copy. A quick and dirty fix (that you should do anyways for performance reasons) is the following:
void list_print(ostream& os, const sorted_list& list)
Now, your iteratornclass takes a mutable list, so this won't work as you expect. You will have quite a few methods to change to make this work.
In any case, your real problem is the lack of a proper copy-constructor. Right now, when you "copy" a list, both end up sharing the same elements, but your destructor is written as if each list owns it's own nodes. Define a proper copy operation and it will solve your problem.
More elaborate help on how to solve the problem: (untested)
Change signature:
void list_print(ostream& os, const sorted_list& list);
Declare + define copy constructor:
sorted_list::sorted_list (const sorted_list& other);
Change iteratorn interface to support a const sorted_list:
class sorted_list::iteratorn
{
public:
iteratorn();
~iteratorn();
iteratorn(const sorted_list& list);
const list_link* list_begin() const;
bool iterator_end() const;
void iterator_next();
int iterator_get_key() const;
double iterator_get_value() const;
private:
// You *should* make this `const` but it is not required.
sorted_list::list_link* current;
};
As you can see, the changes are rather minimal, but need to be applied in various places.
const + non-const iterators:
I applied changes here based on the fact that your iteratorn was currently only defining read-only operations on your sorted_list. If you want to support write access to allow changing the value stored in list nodes (never allow changing the key or you won't have a sorted list anymore), you should define two iterator classes. See the STL iterator interface for more details.
You're copying the list by value, so the local copy in list_print() destructs at end of scope. Pass it by const-reference instead.
This in turn means you will have to change your sorted_list to support working with const lists. In particular you need to have a function that returns a const iterator pointing to the beginning of the list:
sorted_list::const_iteratorn begin() const
{
// returns a const_iteratorn pointing at the beginning of this list
}
Notice you need a new kind of iterator: a const_iteratorn, which promises it won't change the list.
Then, inside print_list() initialize a const_iteratorn with the start iterator that sorted_list returns, by copy:
sorted_list::const_iteratorn s(list.begin());
Finally create a second iterator instance that initializes with an end iterator coming from a member function of sorted_list, similar to the begin() function. This will maintain the const-correctness in print_list().
sorted_list::const_iteratorn e(list.end());
while( s != e ) { // two iterators should be able to compare
// ...
s.iterator_next(); // consider ++s
}
Also, as André mentioned, the fact you don't have a proper copy-constructor and assignment operator is a severe issue. Make sure that copying a sorted_list means copying all its elements, so that the new object owns its own list of elements. Do recall the Rule of Three.