Queue using Class in C++ - c++

I have a problem with queue, with deleting first elements. Pointer "nastepca" should store address of next structure variable in a queue, but it stores nullptr for all of data in the structure and I cannot fixed it. I have tried many option but none of them worked. Is my queue works properly, is it put data in correct way, address alongside address before?
dolacz() - means add/equeue
zdejmij() - means delete/dequeue
koniec - means end
#include <iostream>
#include <string>
using namespace std;
template<typename T>
class Kolejka
{
struct Element
{
T dane;
Element* nastepca;
Element(const T& dane, Element* nastepca) : dane(dane), nastepca(nastepca) {}
};
Element* start = nullptr; //pusty wskaznik nullptr
Element* koniec = nullptr; //pusty wskaznik nullptr
int licz_elementow = 0;
public:
void dolacz(const T& dane)
{
if (start == nullptr) //jezeli kolejka jest pusta to dodaj na poczatek (poczatek i koniec jest ten sam)
{
start = new Element(dane, start);
licz_elementow++;
}
else
{
ss++;
koniec = new Element(dane, koniec);
licz_elementow++;
}
}
void zdejmij() //nie działa
{
if (start == koniec)
{
start = koniec = nullptr;
licz_elementow--;
}
else
{
Element* tmp = start;
start = start->nastepca;
delete tmp;
licz_elementow--;
}
}
T& gora() //zwroci referencje typu T na początek kolejki
{
if (koniec == nullptr)
{
throw runtime_error("Pusta kolejka!");
}
return start->dane;
}
T& tyl() //zwroci referencje typu T na koniec kolejki
{
if (koniec == nullptr)
{
throw runtime_error("Pusta kolejka!");
}
return koniec->dane;
}
int rozmiar()
{
return licz_elementow;
}
bool pusty()
{
return start == nullptr;
}
};
class Auto
{
string marka;
friend ostream& operator <<(ostream& w, Auto& a); //zaprzyjaźniona funkcja przeciążająca operator <<
public:
Auto(string marka): marka(marka) {}
};
ostream& operator <<(ostream& w, Auto& a)
{
w << a.marka;
return w;
}
int main()
{
Kolejka <Auto> kol;
try
{
kol.dolacz(Auto("aaa"));
kol.dolacz(Auto("bbb"));
kol.dolacz(Auto("ccc"));
kol.dolacz(Auto("ddd"));
kol.zdejmij();
cout << "Liczba elementow: ";
cout << kol.rozmiar();
cout << endl;
cout << "Poczatek kolejki: ";
cout << kol.gora();
cout << endl;
cout << "Koniec kolejki: ";
cout << kol.tyl();
}
catch (runtime_error& BLAD)
{
cout << BLAD.what();
}
}
enter image description here
While debugging:
nastepca always have 0x00000000

start = new Element(dane, start);
should be
start = koniec = new Element(dane, nullptr);
When you add the first element, you should change the first and last pointers.
koniec = new Element(dane, koniec);
should be
Element* temp = new Element(dane, nullptr);
koniec->nastepca = temp;
koniec = temp;
When you add a new element (apart from the first) you need to make the old last element point to the new last element.
Pointer operations are tricky, you have to think carefully about what you are really doing. It might help to draw diagrams of the operations you have coded. That way you'd qucikly see that what you coded wasn't right.

Related

BTS tree search function C++

This program reads a CSV file and enters it into a binary search tree. So far I have managed to insert a new node, put it in order, however, internally, perform a search asking for the Varibale Key but I have not managed to get it to work, could someone help me?
The CSV file is reading a file that includes:
1, name1,123456
2, name2,165151
3, name3,1566516
#include <iostream>
#include <iomanip>
#include <fstream>
#include <memory>
#include <string>
#include <sstream>
#include <vector>
struct Person {
int key;
std::string name;
int num;
};
struct Node : Person {
Node(const Person &person) : Person(person) {}
std::unique_ptr<Node> left, right;
void insert(const Person &person);
};
void Node::insert(const Person &person) {
/* recur down the tree */
if (key > person.key) {
if (left)
left->insert(person);
else
left = std::make_unique<Node>(person);
} else if (key < person.key) {
if (right)
right->insert(person);
else
right = std::make_unique<Node>(person);
}
}
std::vector<Person> persons;
void inorder(Node *root) {
if (root) {
// cout<<"\t";
inorder(root->left.get());
std::cout << '\t' << root->key << ' ' << root->name << ' ' << root->num << '\n';
inorder(root->right.get());
}
}
Node *minValueNode(Node *node) {
Node *current = node;
/* loop down to find the leftmost leaf */
while (current && current->left) current = current->left.get();
return current;
}
int main() {
std::unique_ptr<Node> root;
std::ifstream fin("data.txt");
if (!fin) {
std::cout << "File not open\n";
return 1;
}
std::string line;
const char delim = ',';
while (std::getline(fin, line)) {
std::istringstream ss(line);
Person person;
ss >> person.key;
ss.ignore(10, delim);
std::getline(ss, person.name, delim);
ss >> person.num;
if (ss) persons.push_back(person);
}
for (unsigned int i = 0; i < persons.size(); i++) {
std::cout << std::setw(5) << persons[i].key << std::setw(20)
<< persons[i].name << std::setw(15) << persons[i].num << '\n';
if (!root) root = std::make_unique<Node>(persons[i]);
else root->insert(persons[i]);
}
std::cout << "\n\nInorder:\n";
// cout<<node.name;
inorder(root.get());
return 0;
}
/*bool busqueda(Node *root, int dato)
{
if(root){
return false;
}
else if(root->key==dato){
return true;
}
else if(dato<root->key){
busqueda(root->left.get(),dato);
}
else{
return busqueda(root->right.get(),dato);
}
}*/
Presuming that this is the function you need help with:
bool busqueda(Node *root, int dato) {
if(root){
return false;
}
else if(root->key==dato){
return true;
}
else if(dato<root->key){
busqueda(root->left.get(),dato);
}
else{
return busqueda(root->right.get(),dato);
}
}
There are a few issues here:
if(root) {
This is testing if root is not nullptr, so you're going to immediately bail if root points at something. Meaning, you will immediately return false if any tree is provided, which is the opposite of what you want. Even worse, if root is null you will proceed and try to dereference a null pointer, which will crash the program. Change this line to if (root != nullptr) {.
else if(dato<root->key){
busqueda(root->left.get(),dato);
}
This recursive call looks right except you don't return the result meaning that you will reach the end of a function returning bool without actually returning anything meaningful. Just add return before this function call.
You can mostly express this function using boolean logic as well:
bool busqueda(Node *root, int dato) {
return root != nullptr && (
root->key == dato ||
busqueda((dato < root->key ? root->left : root->right).get(), dato)
);
}
This makes it impossible to forget to return something.
As a side note, enable all compiler warnings -- it would have warned you about reaching the end of a non-void function without returning a value.

How to test the given ADT implementation with templates such as <int, int> and <string, int>?

I am working on a problem that requires the implementation of two ADT's. After Implementing, I need to test my bag implementations with the following template combinations:
<int, string>-- all functions
<string, int> -- insert and find functions only
My testing so far has been entering integers to test the different functions. I do not understand what it means to test the implementations with the templates.
Here is my bagADT implementation:
#include <stdlib.h>
#include "bagADT.h"
template <typename E>
class ABag : public Bag<E> {
private:
int maxSize;
int listSize;
E* listArray;
public:
ABag(int size = defaultSize) { // Constructor
maxSize = size;
listSize = 0;
listArray = new E[maxSize];
}
~ABag() { delete[] listArray; } // Destructor
bool addItem(const E& item) {
if (listSize >= maxSize) {
return false;
}
listArray[listSize] = item;
std::cout << "Add Item: Added " << item << " in spot " << listSize << std::endl;
listSize++;
return true;
}
bool remove(E& item) {
for (int i = 0; i < listSize; i++) {
if (listArray[i] == item) {
std::cout << "Remove: Removed " << item << " from position ";
item = i;
std::cout<< item << " and adjusted the location of all other elements." << std::endl;
for (i= item; i < listSize; i++) {
listArray[i] = listArray[i + 1];
}
listSize--;
return true;
}
}
return false;
}
bool removeTop(E& returnValue) {
if (listSize == 0) {
return false;
}
else {
returnValue = listArray[listSize - 1];
std::cout << "Remove Top: Removed " << returnValue << " from the top of the stack." << std::endl;
for (int i = listSize; i < maxSize; i++) {
listArray[i] = listArray[i + 1];
}
listSize--;
return true;
}
}
bool find(E& returnValue) const {
for (int i = 0; i < (listSize - 1); i++) {
if (listArray[i] == returnValue) {
returnValue = i;
return true;
}
}
return false;
}
bool inspectTop(E& item) const {
if (listSize == 0) {
return false;
}
else {
item = listArray[listSize - 1];
std::cout << "Inspect Top: The value on top is currently " << item << "." << std::endl;
return true;
}
}
void emptyBag() {
delete[] listArray;
listSize = 0;
listArray = new E[maxSize];
std::cout << "Empty Bag: Emptied the bag." << std::endl;
}
bool operator+=(const E& addend) {
if (listSize < maxSize) {
return true;
}
return false;
}
int size() const {
std::cout << "Size: Number of elements in listArray: " << listSize << std::endl;
return (listSize - 1);
}
int bagCapacity() const {
std::cout << "Bag Capacity: The capacity of this bag is " << maxSize << std::endl;
return maxSize;
}
};
Here is another file provided by my professor called kvpairs:
#ifndef KVPAIR_H
#define KVPAIR_H
// Container for a key-value pair
// Key object must be an object for which the == operator is defined.
// For example, int and string will work since they both have == defined,
// but Int will not work since it does not have == defined.
template <typename Key, typename E>
class KVpair {
private:
Key k;
E e;
public:
// Constructors
KVpair() {}
KVpair(Key kval, E eval)
{
k = kval; e = eval;
}
KVpair(const KVpair& o) // Copy constructor
{
k = o.k; e = o.e;
}
void operator =(const KVpair& o) // Assignment operator
{
k = o.k; e = o.e;
}
bool operator==(const KVpair& o) const {
if (o.k == k) {
return true;
}
return false;
}
//The following overload is provided by Adam Morrone, Spring 2016 class.
//Thanks Adam :)
friend ostream& operator<<(ostream& os, const KVpair& o) // output print operator
{
os << "Key: " << o.k << " Value: " << o.e;
return os;
}
// Data member access functions
Key key() { return k; }
void setKey(Key ink) { k = ink; }
E value() { return e; }
};
#endif
I am expected to show the test outputs using the above templates, but I have no idea how to do this. Also, ignore the += overload. It is incorrect and I know. I am supposed to overload it to directly add a new int to the array.
I think I understand now. I could be wrong, but this is my guess.
Your bag is singly templated, but it will be holding KVpair. They said they will use KVpair with <int, string> and <string, int>.
When they talk about testing it, that means they will be instantiating it as follows:
int main() {
ABag<KVPair<int, string>> bag;
bag.addItem(KVpair(1, "hi"));
//...
}
This is what I am pretty sure they mean by "testing it with templates".
As a minor edit, I don't know what C++ version you are using but if it's very archaic, you might need to write template instantiation like ABag<KVPair<int, string> > instead of putting them together. I remember vaguely this being an issue a long time ago.

Writing a priority queue with a max heap structure in c++

I am writing a priority queue with a max heap structure as an assignment for school. I can either write it as an array or I can use a vector. I chose a vector. So the assignment is this, the user chooses options from a menu where he either wants to add,print, or view the elements. When the user chooses to add he gets ask who wants to be added, the instructor, student, or TA. He can enter i,I,t,T,S,s. The instructor having the highest priority where if the user chooses the option to print and there is an instructor in the queue he gets to go first. The TA having the second highest priority where if there is a TA and a student in the queue, the TA goes first. If there is is more than one instructor than the queue acts as a normal queue. I have written most of it, or tried. I got my max heap implementation from my textbook since they provide one. Now the problem is this, when I have more than one item in the priority queue and I choose to print, it crashes and gives me a vector subscript out of range exception. I been trying to fix it and no luck. Also, when I try to print the elements in the queue or print them, it needs to say the job# with the name of the person. Can someone help me find a way to implement that.
#pragma once
#include <vector>
struct Heap
{
std::vector<int> m_elements;
void ReHeapDown(int, int);
void ReHeapUp(int, int);
void Swap(int& a, int& b);
};
#include "heap.h"
void Heap::ReHeapDown(int index, int bottom)
{
int maxChild, rightChild, leftChild;
leftChild = index * 2 + 1;
rightChild = index * 2 + 2;
if (leftChild <= bottom)
{
if (leftChild == bottom)
maxChild = leftChild;
else
{
if (m_elements[leftChild] <= m_elements[rightChild])
maxChild = rightChild;
else
maxChild = leftChild;
}
if (m_elements[index] < m_elements[maxChild])
{
Swap(m_elements[index], m_elements[maxChild]);
ReHeapDown(maxChild, bottom);
}
}
}
void Heap::ReHeapUp(int index, int bottom)
{
int parent;
if (bottom > index)
{
parent = (bottom - 1) / 2;
if (m_elements[parent] < m_elements[bottom])
{
Swap(m_elements[parent], m_elements[bottom]);
ReHeapUp(index, parent);
}
}
}
void Heap::Swap(int& a, int& b)
{
int temp;
temp = a;
a = b;
b = temp;
}
#include <iostream>
#include "heap.h"
#pragma once
class PQTYPE
{
private:
Heap m_Items;
public:
bool isEmpty() const;
void Enqueue(int, std::string);
void Dequeue(int, std::string);
void printElements();
};
#include "pqtype.h"
bool PQTYPE::isEmpty() const
{
return m_Items.m_elements.empty();
}
void PQTYPE::Enqueue(int newItem, std::string lName)
{
if (lName == "Student")
{
m_Items.m_elements.push_back(newItem);
m_Items.ReHeapUp(0, m_Items.m_elements.size() - 1);
}
else if (lName == "TA")
{
m_Items.m_elements.push_back(newItem);
m_Items.ReHeapUp(0, m_Items.m_elements.size() - 1);
}
else if (lName == "Instructor")
{
m_Items.m_elements.push_back(newItem);
m_Items.ReHeapUp(0, m_Items.m_elements.size() - 1);
}
}
void PQTYPE::Dequeue(int item, std::string lName)
{
if (isEmpty())
std::cout << "No jobs to print\n";
else
{
m_Items.m_elements[0] = m_Items.m_elements.back();
std::cout << "Now printing Job#" << m_Items.m_elements[item - 1] << " " << lName.c_str() << std::endl;
m_Items.m_elements.pop_back();
m_Items.ReHeapDown(0, item - 1);
}
}
void PQTYPE::printElements()
{
if (isEmpty())
std::cout << "No jobs to print\n";
else
{
for (int i = 0; i < m_Items.m_elements.size(); i++)
{
std::cout << "Job#" << m_Items.m_elements[i] << std::endl;
}
}
}
#include"pqtype.h"
struct Person
{
int m_priority;
std::string m_name;
Person()
{
m_priority = 0;
m_name = " ";
}
};
int showMenu();
void addJobs(PQTYPE&, Person&);
void printJobs(PQTYPE&, Person&);
void viewJobs(PQTYPE&);
int main()
{
int option;
Person p;
PQTYPE pq;
do
{
option = showMenu();
switch (option)
{
case 1: addJobs(pq, p);
break;
case 2: printJobs(pq, p);
break;
case 3: viewJobs(pq);
break;
case 4:
break;
default: std::cout << "Wrong input\n";
break;
}
} while (option != 4);
return 0;
}
int showMenu()
{
int choice;
std::cout << " 1.)Add Job\n";
std::cout << " 2.)Print Job\n";
std::cout << " 3.)View Jobs\n";
std::cout << " 4.)Exit\n";
std::cout << " Enter Choice: ";
std::cin >> choice;
return choice;
}
void addJobs(PQTYPE& pq, Person& per)
{
char jobChoice;
std::cout << "Who is the job for ( Instructor(i or I), TA(t or T), Student(s or S) :";
std::cin >> jobChoice;
if (jobChoice == 'S' || jobChoice == 's')
{
per.m_priority++;
per.m_name = "Student";
pq.Enqueue(per.m_priority, per.m_name);
}
else if (jobChoice == 'T' || jobChoice == 't')
{
per.m_priority++;
per.m_name = "TA";
pq.Enqueue(per.m_priority, per.m_name);
}
if (jobChoice == 'I' || jobChoice == 'i')
{
per.m_priority++;
per.m_name = "Instructor";
pq.Enqueue(per.m_priority, per.m_name);
}
}
void printJobs(PQTYPE& pq, Person& p)
{
pq.Dequeue(p.m_priority, p.m_name);
}
void viewJobs(PQTYPE& pq)
{
pq.printElements();
}
In your original code the index used inside Dequeue() for accessing the vector doesn't seem to be initialised in the right way. Let's assume that you have added two entries to your list. In this case the value of P.m_priority inside your main() is 2. Now you're calling printJobs() for the first time. printJobs() calls pq.Dequeue(p.m_priority, p.m_name), so Dequeue() gets p.m_priority as its parameter item. Keep in mind that item has the value 2.
m_Items.m_elements[0] = m_Items.m_elements.back();
std::cout << "Now printing Job#" << m_Items.m_elements[item - 1] << " " << lName.c_str() << std::endl;
m_Items.m_elements.pop_back();
You're accessing your std::vector using an index of item - 1. This works for the first time, as there are two elements in your list. In this call, there is also a pop_back() done on your list, which decreases its size by one. The next time you call printJobs(), the given parameter item won't have changed, it still has the value 2. When you access your Itemlist, there is no longer an index of 1, and an subscript out of range exception will be thrown.
There were no fixed priorities assigned to the three entry types in your original version, so I added these (see addJobs() ).
So a possible solution to store the person's name could look like this:
struct Person
{
int m_priority;
std::string m_name;
Person()
{
m_priority = 0;
m_name = " ";
}
};
struct Heap
{
std::vector<Person> m_elements;
void ReHeapDown(int, int);
void ReHeapUp(int, int);
void Swap(Person& a, Person& b);
};
void Heap::ReHeapDown(int index, int bottom)
{
int maxChild, rightChild, leftChild;
leftChild = index * 2 + 1;
rightChild = index * 2 + 2;
if (leftChild <= bottom)
{
if (leftChild == bottom)
maxChild = leftChild;
else
{
if (m_elements[leftChild].m_priority <= m_elements[rightChild].m_priority)
maxChild = rightChild;
else
maxChild = leftChild;
}
if (m_elements[index].m_priority < m_elements[maxChild].m_priority)
{
Swap(m_elements[index], m_elements[maxChild]);
ReHeapDown(maxChild, bottom);
}
}
}
void Heap::ReHeapUp(int index, int bottom)
{
int parent;
if (bottom > index)
{
parent = (bottom - 1) / 2;
if (m_elements[parent].m_priority < m_elements[bottom].m_priority)
{
Swap(m_elements[parent], m_elements[bottom]);
ReHeapUp(index, parent);
}
}
}
void Heap::Swap(Person& a, Person& b)
{
Person temp;
temp = a;
a = b;
b = temp;
}
#include <iostream>
class PQTYPE
{
private:
Heap m_Items;
public:
bool isEmpty() const;
void Enqueue(Person);
void Dequeue();
void printElements();
};
bool PQTYPE::isEmpty() const
{
return m_Items.m_elements.empty();
}
void PQTYPE::Enqueue(Person newItem)
{
if (!newItem.m_name.compare("Student"))
{
m_Items.m_elements.push_back(newItem);
m_Items.ReHeapUp(0, m_Items.m_elements.size() - 1);
}
else if (!newItem.m_name.compare("TA"))
{
m_Items.m_elements.push_back(newItem);
m_Items.ReHeapUp(0, m_Items.m_elements.size() - 1);
}
else if (!newItem.m_name.compare("Instructor"))
{
m_Items.m_elements.push_back(newItem);
m_Items.ReHeapUp(0, m_Items.m_elements.size() - 1);
}
}
void PQTYPE::Dequeue()
{
if (isEmpty())
std::cout << "No jobs to print\n";
else
{
Person front = m_Items.m_elements.front();
std::cout << "Now printing Job#" << front.m_priority << " " << front.m_name.c_str() << std::endl;
m_Items.m_elements.erase(m_Items.m_elements.begin());
m_Items.ReHeapDown(0, m_Items.m_elements.size() - 1);
}
}
void PQTYPE::printElements()
{
if (isEmpty())
std::cout << "No jobs to print\n";
else
{
for (int i = 0; i < m_Items.m_elements.size(); i++)
{
std::cout << "Job#" << m_Items.m_elements[i].m_priority << " " << m_Items.m_elements[i].m_name.c_str() << std::endl;
}
}
}
int showMenu();
void addJobs(PQTYPE&, Person&);
void printJobs(PQTYPE&, Person&);
void viewJobs(PQTYPE&);
int showMenu()
{
int choice;
std::cout << " 1.)Add Job\n";
std::cout << " 2.)Print Job\n";
std::cout << " 3.)View Jobs\n";
std::cout << " 4.)Exit\n";
std::cout << " Enter Choice: ";
std::cin >> choice;
return choice;
}
void addJobs(PQTYPE& pq, Person& per)
{
char jobChoice;
std::cout << "Who is the job for ( Instructor(i or I), TA(t or T), Student(s or S) :";
std::cin >> jobChoice;
if (jobChoice == 'S' || jobChoice == 's')
{
per.m_priority = 0;
per.m_name = "Student";
pq.Enqueue(per);
}
else if (jobChoice == 'T' || jobChoice == 't')
{
per.m_priority = 1;
per.m_name = "TA";
pq.Enqueue(per);
}
if (jobChoice == 'I' || jobChoice == 'i')
{
per.m_priority = 2;
per.m_name = "Instructor";
pq.Enqueue(per);
}
}
void printJobs(PQTYPE& pq)
{
pq.Dequeue();
}
void viewJobs(PQTYPE& pq)
{
pq.printElements();
}
int main()
int option;
Person p;
PQTYPE pq;
do
{
option = showMenu();
switch (option)
{
case 1: addJobs(pq, p);
break;
case 2: printJobs(pq);
break;
case 3: viewJobs(pq);
break;
case 4:
break;
default: std::cout << "Wrong input\n";
break;
}
} while (option != 4);
return 0
}
Are you sure that the methods ReHeapUp and ReHeapDown meet your requirements? And shouldn't there be a distinction between job number and priority?

Doubly linked list assignment operator (withoud using std::swap)

So I've got to implement doubly linked list. I've got constructor, destructor, many other methods like inserting too. But as I would like to apply to The Rule of Three I would need to implement copy assignment operator and a copy constructor. I've seen many solutions on the internet, but all of them required std::swap and std::move or std::copy. I cannot use any of those as all my algorithms have to be done without STL or boost::. I know I have to start like this:
list & list::operator=(const list & that)
{
if (this != &that)
{
}
return *this;
}
Then I would to something like this:
if (this != &that)
{
this->clear();
node *t = this->head;
node *o = that.head;
while (o->next)
{
t.append(o.value);
o = o.next;
t = t->next
}
}
return *this;
#Edit:
void menu_list()
{
char opt;
int *t;
list myList = list();
do {
display_menu("--- LISTA ---");
opt = _getche();
std::cout << '\n';
switch (opt) {
case '1': //reading a list from a text file
{
int elements;
std::ifstream file_stream;//
file_stream.open("data.txt"); //
if (!file_stream.good()) //
{
std::cout << "Nie udalo sie wczytac pliku!" << '\n';
break;
}
file_stream >> elements; //
t = new int[elements]; // stworzenie listy
for (int i = 0; i<elements; i++)
{
file_stream >> *(t + i); //
}
myList = list(t, elements); //zainicjalizowanie listy
delete[] t; //
std::cout << "\nObecny stan listy: "; std::cout << myList.to_string() << '\n';
break;
}
case '2': //deleting an element from a list
{
if (myList.is_empty())
{
std::cout << "Lista jest pusta! \n"; break;
}
int index;
std::cout << "Podaj indeks elementu do usuniecia: ";
scanf_s("%d", &index);
if (myList.size() <= index)
{
std::cout << "Lista nie ma tylu elementow!\n"; break;
}
else if (index < 0)
{
std::cout << "Indeks nie moze byc mniejszy od 0!\n"; break;
}
myList.remove(index);
std::cout << "\nObecny stan listy: "; std::cout << myList.to_string() << '\n';
break;
}
list.cpp (implementation of list header):
#include "list.h"
#include <ctime>
list::list() : head(nullptr), tail(nullptr), n(0)
{
}
list::list(int* t, int n) : head(nullptr), tail(nullptr), n(0)
{
if (!this->is_empty())
{
this->clear();
}
for (int i = 0; i < n; i++)
{
this->append(t[i]);
}
}
list::list(const list& obj)
{
}
clear() is a method that clears the whole list (and head and tail = nullptr) and append() is a method that appends a value at the end of a list.
Now, I guess this is wrong, but I don't have any clue how to make it work and I'd like to ask you guys how can I do it

C++ simple linked list attempt : don't quite understand what's going on

I tried to strictly implement in c++ what I'm studying in algorithmic at the moment, recursive functions with simple linked lists. Here is what I've come by :
#include <iostream>
using namespace std;
class Liste {
private :
int val;
Liste *suivante;
public :
Liste(int val = 0, Liste *suivante = NULL) {
this->val = val;
this->suivante = suivante;
}
void afficherElement() const {
cout << "Adresse : " << this << endl;
cout << "Valeur : " << val << endl;
cout << "Adresse suivante : " << suivante << endl;
cout << endl;
}
int tete() const {
return val;
}
Liste reste() const {
return *suivante;
}
bool estVide() const {
return (suivante == NULL);
}
Liste prefixer(int val) {
Liste *nouvelle = new Liste(val, this);
return *nouvelle;
}
Liste suffixer(int val) {
suivante = new Liste(val);
afficherElement(); // test (last element won't be displayed)
return *suivante;
}
};
int main() {
Liste uneListe(3); // 1st element
uneListe.suffixer(5).suffixer(8).suffixer(10); // adding 3 more
cout << "-----------\n\n";
uneListe.afficherElement(); // displaying 1st element : ok
uneListe.reste().afficherElement(); // displaying 2nd element : pointer is NULL !!???
// uneListe.reste().reste().afficherElement(); --> segmentation fault, predictably enough
return 0;
}
As you can guess, it doesn't work. When I add elements, calling the display method within the add method, elements seem to be well formed although the pointer value and the next element's adress differ (I don't get why).
But, after adding process is done, I try to display the list again, 1st element is well linked with 2nd, but then there is a NULL pointer value. Wonder why ??
I've seen a code with two classes (Node and List), that works fine, but I'd like to know what is wrong with mine. Is it that I'm creating new objects of a class within this same class ?
Thanks,
for right this problem you most change this line
Liste suffixer(int val)
to
Liste* suffixer(int val)
and then change this line
return *suivante;
to
return suivante;
and in main use this line
uneListe.suffixer(5)->suffixer(8)->suffixer(10);
instead of
uneListe.suffixer(5).suffixer(8).suffixer(10);
Your class methods Liste::prefixer(int val) and Liste suffixer(int val) will return a copy of the object created, they should return a pointer to the object (or a reference).
e.g.
Liste *Liste::suffixer(int val){
if(suivante == nullptr)
suivante = new Liste(val);
else
throw std::runtime_error("Generic error message");
return suivante;
}
or
Liste &Liste::suffixer(int val){
... previous inner method ...
return *suivante;
}
Class Liste contains a value and a reference, which is not what a list is: a singly linked list is a pointer to an element containing a value and a pointer to the next node.
You might use a value+pointer element as a list object, ignoring the val member. This would require different coding for some methods, e.g., for tete() and reste().
But, since using
typedef Liste * real_list_type;
is what you have in mind (? - see below), let's look at the methods.
bool estVide() const { return (suivante == NULL); }
This is in contradiction to the real_list_type being a mere List *; if you compare this to method reste(), it actually tests whether the tail is empty, not the list itself! (It would be in sync with using a value+pointer object as the list object.)
Liste suffixer(int val) { suivante = new Liste(val); ... }
This is bad: it replaces suivante with a new object, no matter what's stored in there (a memory leak). You'll have to do
Liste suffixer(int val) {
if( suivante == NULL ){
suivante = new Liste(val);
} else {
suivante->suffixer( val );
}
return *this;
}
LATER
I think that would be the best way to keep it as close to the abstract concept as possible. Note that there is no "isEmpty" - this is done by a test whether the List * variable representing the list equals NULL, but you can't have a method for that.
template<typename T>
class List {
public:
List( T v, List* t = nullptr ) : value(v), next(t){}
~List(){ delete next; }
List* prepend( T v ){
return new List( v, this );
}
List* append( T v ){
if( next == nullptr ){
next = new List( v );
} else {
next->append( v );
}
return this;
}
T head(){ return value; }
List* tail(){ return next; }
void dump(){
List* curr = this;
std::string del = "";
while( curr != nullptr ){
std::cout << del << curr->value;
del = ", ";
curr = curr->next;
}
std::cout << std::endl;
}
private:
T value;
List* next;
};
int main(){
typedef List<int> * intList;
intList list = new List<int>( 1 );
list->append( 2 )->append( 3 );
list->dump();
}
Here is the "fixed" version of my first attempt :
#include <iostream>
using namespace std;
class Liste {
private :
int val;
bool vide;
Liste *suivante;
public :
Liste(int val = 0, bool vide = true, Liste *suivante = NULL) {
this->val = val;
this->vide = vide;
this->suivante = suivante;
}
void afficherElement() const {
cout << "Adresse : " << this << endl;
cout << "Valeur : " << val << endl;
cout << "Vide : " << vide << endl;
cout << "Adresse suivante : " << suivante << endl;
cout << endl;
}
int head() const {
return val;
}
Liste *reste() const {
return suivante;
}
bool estVide() const {
return vide;
}
Liste *prefixer(int val) {
Liste *nouvelle = new Liste(val, this);
return nouvelle;
}
Liste *suffixer(int val) {
if(suivante == NULL) {
suivante = new Liste(val);
vide = false;
}
return suivante;
}
};
void afficherListe(Liste *uneListe) {
(*uneListe).afficherElement();
if(!(*uneListe).estVide()) {
afficherListe((*uneListe).reste());
}
}
int main() {
Liste *test = new Liste(3);
(*test).suffixer(5);
afficherListe(test);
return 0;
}
As expected it's awfully unpractical.
Laune's solution looks good...
However, the whole thing is bizarre, I suppose I'd be better off sticking with the regular List/Nodes way. Definitely gonna talk about that with my teacher.