Unique pointer in Linked List - Unhandled exception, stack overflow - c++

First of all, let me say thank you for all the help I've been received in the last couple hours. I've been struggle with this problem, how to convert from raw pointer to unique pointer and got myself into a lot of errors. However, with the help of this community, I've been thankful that my program finally compiles without errors at all. But I'm not there yet, I guess. I feel like I'm like one minute away from the finish line, so I won't give up till I can solve it. My program crashes as soon as it runs, it says stack overflow and throw out the exception. I guess it must be the way that I declare and initialize the unique pointer as a class member in the constructor is not correct at all and therefore it crashes right from the minute it calls the constructor. Would anyone please tell me what I should do to fix this error ? Thanks.
This is my main cpp file:
#include"ContactList.h"
#include<memory>
using namespace std;
int main()
{
//ContactList* cl1 = new ContactList();
unique_ptr<ContactList> cl1(new ContactList());
string name;
while(true)
{
cout << "Enter a name or q to quit: " << endl;
cin >> name;
if(name == "q")
break;
cl1->addToHead(name);
}
cl1->PrintList();
return 0;
}
ContactList.h
#pragma once
#include"Contact.h"
#include<memory>
using namespace std;
class ContactList
{
public:
ContactList();
void addToHead(const std::string&);
void PrintList();
private:
//Contact* head;
unique_ptr<Contact> head;
int size;
};
ContactList.cpp
#include"ContactList.h"
#include<memory>
using namespace std;
ContactList::ContactList(): head(new Contact()), size(0)
{
}
void ContactList::addToHead(const string& name)
{
//Contact* newOne = new Contact(name);
unique_ptr<Contact> newOne(new Contact(name));
if(head == 0)
{
head.swap(newOne);
//head = move(newOne);
}
else
{
newOne->next.swap(head);
head.swap(newOne);
//newOne->next = move(head);
//head = move(newOne);
}
size++;
}
void ContactList::PrintList()
{
//Contact* tp = head;
unique_ptr<Contact> tp(new Contact());
tp.swap(head);
//tp = move(head);
while(tp != 0)
{
cout << *tp << endl;
tp.swap(tp->next);
//tp = move(tp->next);
}
}
Contact.h
#pragma once
#include<iostream>
#include<string>
#include<memory>
class Contact
{
friend std::ostream& operator<<(std::ostream& os, const Contact& c);
friend class ContactList;
public:
Contact(std::string name = "none");
private:
std::string name;
//Contact* next;
std::unique_ptr<Contact> next;
};
Contact.cpp
#include"Contact.h"
using namespace std;
Contact::Contact(string n):name(n), next(new Contact())
{
}
ostream& operator<<(ostream& os, const Contact& c)
{
return os << "Name: " << c.name;
}
This is the error I get:
Unhandled exception at 0x77E3DEFE (ntdll.dll) in Practice.exe: 0xC00000FD: Stack overflow (parameters: 0x00000001, 0x002B2F58).

You didn't post the code for Contact, but I assume it's the same as in one of your previous questions:
Contact::Contact(string n):name(n), next(new Contact())
{
}
As you can see, constructing a Contact requires setting its next member to a new Contact.
In order to construct that Contact, you're going to create a new Contact for its next member.
And so on, et cetera, to infinity and beyond.
This is the cause of of the stack overflow - Contact construction never ends.
You probably don't want next to be anything in particular for a newly constructed Contact, so try
Contact::Contact(string n):name(n), next(0)
{
}

You have a problem in your ContactList::PrintList() method: you don't need unique_ptr when you iterate through some items just observing them.
When observing items, raw pointers are just fine.
In general, owning raw pointers are not good (except in some special cases), but observing raw pointers are just fine.
In addition, note also that in your ContactList default constructor, you don't need to allocate an empty Contact with new and assign it to head unique_ptr data member: unique_ptr default constructor will automatically initialize head to a nullptr.
Note also that the ContactList::PrintList() methods should be marked as const for proper const-correctness, since usually printing the content of some collection should not alter the items in the collection.
Finally, the ContactList allocation in your main() function can be simply done on the stack:
ContactList cl;
There's no need to use unique_ptr in this case (please program in C++, not in Java or C# style).
And, a style note: I don't like that some methods start with upper-case letter (e.g. PrintList()) and others with lower-case letter (e.g. addToHead()): choose one style, and be coherent with it (at list at the source file level, if not at the whole project level).
Below there's a single source file test code, based on your code with some fixes applied.
I compiled it and tested a bit with VC10 (Visual Studio 2010 SP1); it seems to work:
C:\Temp>test.exe
Enter a name or q to quit:
Bob
Enter a name or q to quit:
Jane
Enter a name or q to quit:
John
Enter a name or q to quit:
Mary
Enter a name or q to quit:
q
[Contact name: Mary]
[Contact name: John]
[Contact name: Jane]
[Contact name: Bob]
Compilable source code follows:
#include <iostream>
#include <memory>
#include <ostream>
#include <string>
using namespace std;
// "Imaginary" Contact implementation (you didn't provide it)
struct Contact {
Contact() {}
explicit Contact(const string& n) : name(n) {}
string name;
unique_ptr<Contact> next;
};
ostream& operator<<(ostream& os, const Contact& c) {
os << "[Contact name: " << c.name << "]";
return os;
}
class ContactList {
public:
ContactList();
void AddToHead(const string&);
void PrintList() const;
private:
unique_ptr<Contact> head;
int size;
};
ContactList::ContactList()
: size(0) {
// No need to initialize head pointer.
// It will be automatically initialized to nullptr.
}
void ContactList::AddToHead(const string& name) {
unique_ptr<Contact> newOne(new Contact(name));
if(head == 0) {
head.swap(newOne);
} else {
newOne->next.swap(head);
head.swap(newOne);
}
size++;
}
void ContactList::PrintList() const {
const Contact * pContact = head.get();
while (pContact != nullptr) {
cout << *pContact << endl;
pContact = pContact->next.get();
}
}
int main() {
// No need to allocate ContactList using unique_ptr.
// Stack scoped-based allocation is just fine.
ContactList cl;
while (true) {
cout << "Enter a name or q to quit: " << endl;
string name;
cin >> name;
if (name == "q")
break;
cl.AddToHead(name);
}
cl.PrintList();
}

Related

Redefine a Node of linked list: std::string

I am currently working on a school project, the material is new to me at the moment, basically, we are creating a Robot Guider that tracks their movement, distance, speed, etc... one of the functions that we are required to make is renaming a robot, however, they are stored in Node.
I have spent some time looking around for a quick solution and I am a little confused by the examples online. If someone could please help but also explain their logic that would be greatly appreciated.
we are using two different classes to track all of the information
-----CLASS #1:
#ifndef RobotList_hpp
#define RobotList_hpp
#include "Robot.hpp"
#include <stdio.h>
#include <iostream>
class RobotList{
private:
class Node{
public:
Robot* val;
Node* next = nullptr;
Node(std::string aName) {
val = new Robot;
val->setName(aName);
}
};
Node* head = nullptr;
Node* tail = nullptr;
public:
RobotList() = default;
~RobotList();
void display() const;
bool isEmpty();
Robot* find_nth();
void updateList();
void addNode(std::string name);
void deleteNode(std::string name);
void rename();
void robotDist() const;
};
#endif /* RobotList_hpp */
---CLASS #2:
#ifndef Robot_hpp
#define Robot_hpp
#include <stdio.h>
#include <iostream>
#include <algorithm>
class Robot{
private:
int x, y, curSpeed, totDist;
std::string name; char lastCommand;
bool stop_; int off_or_on;
public:
std::string getName() { return name; }
void setName(std::string a) {
this->name = a;
}
int getTotDist() { return totDist; }
void moveRobot();
int findRobot();
};
#endif /* Robot_hpp */
void RobotList::rename(){
std::string new_name;
std::cout << "Which robot do you want to rename?"<< std::endl;
std::cin >> new_name;
Node* temp = head;
while(!head){
if(temp->val->getName() == new_name){
// update list with user input new_name
// reassign a node that holds a string value
}
}
temp = temp->next; // rest of list til nullptr
}
This is what I tried to do but it was not operating properly.
I wrote out two comments on what I am trying to do. Thanks.
The problem is the while loop.
Head is a pointer to the first element so !head is true only when the list is empty, which is not what you want. Head should not be modified because we will lose the start of the list, that's why we have the temp.
The loop should stop at the end of the list, we know we reached the end when temp is nullptr. This is convenient because it makes sure we never dereference a null pointer. temp = temp->next; should be placed inside the loop so that it doesn't get stuck at the first element.
std::string old_name, new_name;
std::cout << "Which robot do you want to rename?"<< std::endl;
std::cin >> old_name; // name to search for
std::cout << "Enter new name:"<< std::endl;
std::cin >> new_name; // new name for the robot with old_name
Node* temp = head; // temp = first element(node) of the list
while(temp){ // while we haven't reached the end of the list
if(temp->val->getName() == old_name){
temp->val->setName(new_name);
break; // break if you only want to modify the first occurrence
}
temp = temp->next; // move to the next node
}
Also try to use const references for passing objects whenever possible, otherwise you create a lot of unwanted copies.

Fetching value from std::map which is a user defined class object leading to invalid read

For Simplicity I am creating a demo program which simulates the problem.
In below program when I try access map using worksFineWithPointer function everything works fine i.e. by store the address of value fetched from map and results are also expected and when I try to access map using problem function i.e. store the value in object, it does not shows expected results.
I tried running my faulty function with Valgrind it showed lot of invalid read request. But understanding valgrind is quite difficult for me so didn't got much (if an easy book can be sugested or online reference that would be great too). If I comment out the problem function no valgrind errors.
Then I compiled the program with electric fence and it immediately core dumped and I got to know that which line is having a issue which is as below:
#10 0x0000000000401721 in Node::operator= (this=0x7ffccb72e720, obj=...) at test.cc:33
#11 0x0000000000401b83 in A::problem (this=0x7ffccb72e7c8, prefix="test") at test.cc:102
#12 0x0000000000401350 in main () at test.cc:114
(gdb) f 11
#11 0x0000000000401b83 in A::problem (this=0x7ffccb72e7c8, prefix="test") at test.cc:102
102 crawler = crawler.getMap()[ch];
Not sure what is wrong in above line it will just call operator= and values should get copied in crawler object. May be I am missing some concept of C++ here.
Below is the full code
#include <iostream>
#include <map>
using namespace std;
class Node
{
private:
map<char, Node> mymap;
bool test;
public:
Node():test(false){}
bool getTest()
{
return test;
}
void setTest()
{
test = true;
}
map<char, Node>& getMap()
{
return mymap;
}
};
class A {
private:
Node* root;
public:
A()
{
root = new Node();
}
void insert(string word)
{
Node* crawler = root;
for (char ch : word)
{
if (crawler->getMap().find(ch) == crawler->getMap().end())
crawler->getMap()[ch] = Node();
crawler = &crawler->getMap()[ch];
}
crawler->setTest();
}
// This way if I access no valgring errors everything works fine
void worksFineWithPointer(string prefix)
{
Node* crawler = root;
cout << "worksFineWithPointer:" << endl;
for (char ch : prefix)
{
crawler = &crawler->getMap()[ch];
cout << crawler->getTest() << endl;
}
}
// Problematic Function but not able to find why it is wrong
void problem(string prefix)
{
Node crawler = *root;
cout << "Boolean Attribute changed to true when using objects instead of pointer:" << endl;
for (char ch : prefix)
{
crawler = crawler.getMap()[ch];
cout << crawler.getTest() << endl;
}
}
};
int main()
{
A a;
a.insert("test");
a.problem("test");
a.worksFineWithPointer("test");
return 0;
}
Output (When Compiled and ran with Efence no output came as it dumped before printing):
Boolean Attribute changed to true when using objects instead of pointer:
1 <<<< how this changed to true
1 <<<< how this changed to true
1 <<<< how this changed to true
1 <<<< this was anyway true
WorksFineWithPointer:
0
0
0
1
I am missing some basic C++ concept here I guess, it would be great if someone could explain what is the issue with problematic code statement.
In your problem function, you are making copies of the Nodes and then iterating through them. That's not the same thing as iterating through the pointers.
But the real issue I think is with your class design
class Node
{
private:
map<char, Node> mymap;
// ...
};
At this point, Node is an incomplete type, and so you can't have a map of that type. (From c++17, I believe a vector of incomplete types is allowed though). For a map, I think it's ill-formed, so even if your code compiles, it has UB, and why it crashes is irrelevant.
Anyway, I think you'd be better off having a map of char to Node*. Then your class looks like this
class Node
{
private:
map<char, Node*> mymap;
bool test;
public:
// ...
map<char, Node*>& getMap()
{
return mymap;
}
};
And then your functions become
void worksFineWithPointer(string prefix)
{
Node* crawler = root;
cout << "worksFineWithPointer:" << endl;
for (char ch : prefix)
{
crawler = crawler->getMap()[ch];
cout << crawler->getTest() << endl;
}
}
and
void problem(string prefix) // no longer
{
Node crawler = *root;
cout << "Works fine with objects too :)" << endl;
for (char ch : prefix)
{
crawler = *crawler.getMap()[ch];
cout << crawler.getTest() << endl;
}
}
and they should both work fine.
Even better, make mymap a map<char, std::unique_ptr<Node>>.

Access violation writing location when using pointers

I'm trying to write a program that allows a user to input data into a text file to organize class assignments. The user can display the list of assignments, enter an assignment into the file, and search for specific course work that is due. I am having a problem where I get an access violation writing location error and I'm not entirely sure how to fix it. I have looked at previous discussions that are posted but can't quite figure out where I am going wrong in my code.
This is taskList.cpp.
The header file taskList.h is posted after it.
I'm using VS2013.
When I debug the error is posted at line 55 in the taskList.cpp file below
list = new Task[capacity];
#include "taskList.h"
#include "mytools.h"
TaskList::TaskList()
{
capacity = CAP;
list = new Task[capacity];
size = 0;
}
TaskList::TaskList(char filename[])
{
capacity = CAP;
list = new Task[capacity];
size = 0;
//load from file.
ifstream inData;
Task aTask;
char tempName[MAXCHAR];
char tempDescription[MAXCHAR];
char tempDate[MAXCHAR];
inData.open("task.txt");
if (!inData){
cout << "cannot open file";
exit(0);
}
inData.getline(tempName, MAXCHAR, ';');
while (!inData.eof())
{
inData.getline(tempDescription, MAXCHAR, '\n');
inData.getline(tempDate, MAXCHAR, '\n');
aTask.setName(tempName);
aTask.setDescription(tempDescription);
aTask.setDate(tempDate);
addTask(aTask);
inData.getline(tempName, MAXCHAR, ';');
}
inData.close();
;
TaskList::~TaskList()
{
if (list)
{
delete [] list;
list = NULL;
}
}
//Adds a video item to the list
void TaskList::addTask(Task aTask)
{
list[size++] = aTask;
}
//displays the list of videos
void TaskList::showList()
{
int i = 0;
for (i = 0; i < size; i++)
{
list[i].printTask();
}
}
void TaskList::searchList()
{
char searchName[MAXCHAR];
char tempName[MAXCHAR];
int i;
bool found = false;
cout << "Enter the name of the course to search for: ";
cin.getline(searchName, MAXCHAR);
for (i = 0; i < size; i++)
{
list[i].getName(tempName);
if (strstr(searchName, tempName) != NULL)
{
list[i].printTask();
found = true;
}
}
if (found == false)
cout << "No search results." << endl;
}
void TaskList::writeData()
{
ofstream outData;
outData.open("task.txt");
if (!outData)
{
cout << "cannot open file";
exit(0);
}
for (int i = 0; i < size; i++)
list[i].printToFile(outData);
outData.close();
}
//expand array function
void TaskList::expand()
{
char tempName[MAXCHAR];
char tempDescription[MAXCHAR];
char tempDate[MAXCHAR];
capacity += GROWTH;
Task *temp = new Task[capacity];
//copy from old array to new array
for (int i = 0; i < size; i++)
{
list[i].getName(tempName);
list[i].getDescription(tempDescription);
list[i].getDate(tempDate);
temp[i].setName(tempName);
temp[i].setDescription(tempDescription);
temp[i].setDate(tempDate);
}
//delete old array
delete [] list;
list = NULL;
//point ptr to temp
list = temp;
//set temp to NULL
temp = NULL;
}
The header file (taskList.h)
#include <iostream>
#include <fstream>
using namespace std;
const int CAP = 2;
const int GROWTH = 2;
//define class VideoList for array of Videos and its size.
class TaskList
{
private:
Task *list;
int size;
int capacity;
void expand();
public:
//constructors
TaskList();
TaskList(char filename[]);
//destructor
~TaskList();
//database functions
void addTask(Task aTask);
void showList();
void searchList();
void writeData();
};
#endif
Just to be sure that everything is made clear because there are 3 header files, 4 source files, and a text file, I am include the task.h header file and task.cpp source file.
Here is task.h:
#ifndef TASK_H
#define TASK_H
#define _CRT_SECURE_NO_WARNINGS
#include <iostream>
#include <fstream>
#include <iomanip>
#include <string.h>
using namespace std;
const int MAXCHAR = 101;
class Task
{
private:
char *name;
char *description;
char *date;
public:
//defult constructor
Task();
//constructor with parameters
Task(char newName[], char newDescription[], char newDate[]);
//copy constructor
Task(const Task &otherTask);
//Accessor funct
void getName(char returnName[]);
void getDescription(char returnDescription[]);
void getDate(char returnDate[]);
//mutator function
void setName(char newName[]);
void setDescription(char newDescription[]);
void setDate(char newDate[]);
//print function to print a video
void printTask();
void printToFile(ofstream &outFile);
const Task& operator= (const Task& anItem);
};
#endif
Here is the task.cpp file, not sure if this is necessary but I am adding it for clarity:
#include "task.h"
#include <iostream>
using namespace std;
//defult constructor
Task::Task()
{
strcpy(name, "no course name");
strcpy(description, "no task description");
strcpy(date, "no due date");
}
//constructor with parameters
Task::Task(char newName[], char newDescription[], char newDate[])
{
name = new char[strlen(newName) + 1];
description = new char[strlen(newDescription) + 1];
date = new char[strlen(newDate) + 1];
strcpy(name, newName);
strcpy(description, newDescription);
strcpy(date, newDate);
}
//copy constructor
Task::Task(const Task &otherTask)
{
//allocate memory and then copy name
this->name = new char[strlen(otherTask.name) + 1];
strcpy(name, otherTask.name);
//allocate memory and then copy description
this->description = new char[strlen(otherTask.description) + 1];
strcpy(description, otherTask.description);
//allocate memory and then copy date
this->date = new char[strlen(otherTask.date) + 1];
strcpy(date, otherTask.date);
}
//Accessor functions
void Task::getName(char returnName[])
{
strcpy(returnName, name);
}
void Task::getDescription(char returnDescription[])
{
strcpy(returnDescription, description);
}
void Task::getDate(char returnDate[])
{
strcpy(returnDate, date);
}
//mutator functions
void Task::setName(char newName[])
{
strcpy(name, newName);
}
void Task::setDescription(char newDescription[])
{
strcpy(description, newDescription);
}
void Task::setDate(char newDate[])
{
strcpy(date, newDate);
}
//prints a video item
void Task::printTask()
{
cout << name << ';' << description << ';' << date << endl;
}
void Task::printToFile(ofstream &outFile)
{
outFile << name << ';' << description << ';' << date << endl;
}
//assignment operator overloaded
const Task& Task::operator= (const Task& aTask)
{
strcpy(this->name, aTask.name);
this->description = aTask.description;
strcpy(this->description, aTask.description);
this->date = aTask.date;
strcpy(this->date, aTask.date);
return *this;
}
Here is the problem:
char *name;
// ...
strcpy(name, "no course name");
The first line creates a pointer which currently does not point anywhere. Then you tell strcpy to copy that string to where the pointer is pointing, so it writes the string to "nowhere" (in practice: a semi-random memory location). This causes your access violation.
To fix this, replace the code with:
std::string name;
// ...
name = "no course name";
Do the same for description and date. Note that this means you don't need a copy-constructor or copy-assignment operator or destructor; because the default ones behave correctly.
Of course you will need to change your accssor functions (but they were badly designed anyway since the caller cannot prevent a buffer overflow):
std::string getName() const { return name; }
Also, change Task *list; to std::vector<Task> list; and stop using new and delete. The vector correctly manages memory for you.
It is simplest and easiest to do this task without using pointers or manual memory management or C-library functions such as strcpy. You'll halve your code size (at least) and it will be much less prone to error.
You may need #include <string> and #include <vector>.
Since the erroe happens at allocation if an array (list = new Task[capacity]) i guess your problem is in default constructor of Task class. try playing with this constructor a liitle , i suggest allocating yor char arrays (names , descriptions and data) befor filling them.
somecode like name = new Char[14]; (and of course same for the other two)
You have failed to follow the rule-of-five or the rule-of-zero.
The correct thing (rule-of-zero) would be to implement TaskList in terms of std::vector<Task>.
Seeing as your assignment demands that you use a "dynamic array", perhaps they don't want you to use std::vector. This means that you are stuck with manual memory management. This means that you need to correctly implement or remove the following functions:
//You have these
TaskList::TaskList();
TaskList::TaskList(char filename[]);
TaskList::~TaskList();
//You are missing these, this is your problem:
TaskList::TaskList(TaskList const &o); //Copy constructor
TaskList &TaskList::operator=(TaskList const &o); //Copy assignment
TaskList::TaskList(TaskList &&o); //Move constructor
TaskList &TaskList::operator=(TaskList &&o); //Move assignment
If you do not explicitly supply these functions, the compiler may automatically generate them, and the compiler-generated versions will be incorrect (for the situation where you are manually managing resources within TaskList), as they will do member-wise moves or copies, rather than copying or moving the underlying resources. When you then use these incorrect compiler-generated versions, your code will have strange behaviour.
For Task, you shouldn't be managing multiple resources at once. Use std::string, or otherwise write your own string class, and then use it to manage the string members of Task. If you do not, your code is almost guaranteed to be incorrect (due to a lack of exception safety).

c++ linked list storing strings

I am creating a custom linked list class to store strings from a program I created for an assignment. We were given a linked list handout that works for ints and were told to retool it for string storage, however I am running into an error when trying to run it.
I'm getting the error ""terminate called after throwing an instance of 'std::logic_error'
what(): basic_string::_S_construct null not valid"" (which I searched around and found it was because of a string being set to null, however I do not know how to fix the error, I'm guessing it is with line 8 but I've toyed around with it to no success.) I've searched around and looked through the similar questions but could not find anything that helped.
#include <cstdlib>
#include <iostream>
#include <string>
#include <cstdio>
#include <iomanip>
using namespace std;
struct node {
node(string current) { data=current; next=NULL; }
string data;
node *next;
};
class list {
public:
list(int N=0, string current);
~list();
bool empty() const { return N == 0; }
void clear();
void insert(int, const string &);
void push_front(const string &current);
friend ostream & operator<<(ostream &out, const list &current);
private:
int N;
node *head;
node *findnode(int);
};
list::list(int M, string current) {
N = M;
head = new node;
for (int i=0; i<N; i++)
insert(0, current);
}
list::~list() {
clear();
delete head;
}
void list::clear() {
while (!empty()) remove(0);
}
void list::insert(int i, const string &din) {
node *p = new node(din);
node *pp = findnode(i-1);
p->next = pp->next;
pp->next = p;
N++;
}
inline
node *list::findnode(int i) {
if (i == -1)
return head;
node *p = head->next;
while (i--)
p = p->next;
return p;
}
void list::push_front(const string &current) {
head = new node;
head->next;
}
ostream& operator<<(ostream& out, const list& current)
{
out << current;
return out;
}
const string rank[] = { "Ace", "2", "3", "4", "5", "6", "7",
"8", "9", "10", "Jack", "Queen", "King" };
const string suit[] = { "Clubs", "Diamonds", "Hearts", "Spades" };
string random_card(bool verbose=false) {
string card;
card = rank[ rand()%13 ];
card += " of ";
card += suit[ rand()%4 ];
if (verbose)
cout << card << "\n";
return card;
}
int main(int argc, char *argv[])
{
bool verbose = false;
int seedvalue = 0;
string stop_card = "Queen of Hearts";
for (int i=1; i<argc; i++) {
string option = argv[i];
if (option.compare(0,6,"-seed=") == 0) {
seedvalue = atoi(&argv[i][6]);
} else if (option.compare(0,6,"-stop=") == 0) {
stop_card = &argv[i][6];
} else if (option.compare("-verbose") == 0) {
verbose = true;
} else
cout << "option " << argv[i] << " ignored\n";
}
srand(seedvalue);
list deck[4];
while (1) {
string card = random_card(verbose);
char first[10];
char second[10];
sscanf(card.c_str(), "%s of %s", first,second);
// reverse engineer card suit and rank
int index2;
//suit index
for(int i=0; i<4; i++){
if(suit[i]==second){
index2=i;
break;
}
}
deck[index2].push_front(first);
if (card.compare(stop_card)==0){
break;
}
}
// print formatted table contents to stdout
cout << "Clubs : ";
cout << setw(3) << deck[0];
cout << endl;
cout << "Diamonds : ";
cout << setw(3) << deck[1];
cout << endl;
cout << "Hearts : ";
cout << setw(3) << deck[2];
cout << endl;
cout << "Spades : ";
cout << setw(3) << deck[3];
cout << endl;
}
The following are significant problems that will either hinder building (read: compile-time bugs) or actual runtime. This makes no claim these are all the bugs, but its certainly worth considering. I should note right off the top that the concept of a "sentinel" head-node allocation is almost- never needed in linked list management, and this code is not one of the exceptions. If the list is "empty" head should be null. If it isn't empty, head should not be null. Its just that simple, and this code would be leaps-and-bounds simpler if that were followed.
With that, read on.
Invalid Code:
list(int N=0, string current);
Reason: C++ requires all arguments following the first argument that is provided a default value to also have default values. This would be valid if N was the second parameter, or if current was also given a default value (or of course ,if neither had default values). All of the following are valid:
list(int N, string current);
list(int N, string current = "");
list(int N=0, string current = "");
As-written, it will fail to compile.
Invalid code: No matching constructor available
head = new node;
Reason: The structure node does not defined a default-compliant constructor (one that either has no parameters, or all parameters with default value provisions) but does specify a non-default constructor (one that requires at least one parameter). As a result, the language-supplied default constructor is not auto-generated and there is no node::node() constructor to be found.
Incorrect Code: Expression result is unused
void list::push_front(const string &current) {
head = new node;
head->next; // THIS LINE
}
Reason: This code blindly overwrites whatever is currently occupied in the head pointer with a new (invalid, see above for why) node allocation. Anything that was in head prior is leaked forever, and current is unused whatsoever. Fix this by allocating a new node with current as the value, settings its next pointer to head and head to the new node:
void list::push_front(const string &current)
{
node *p = new node(current);
p->next = head;
head = p;
}
Infinite Recursion
ostream& operator<<(ostream& out, const list& current)
{
out << current;
return out;
}
Reason: This code literally invokes itself. Recursively. Forever (well, until you run out of call-stack space).
NULL Pointer Dereference
inline node *list::findnode(int i)
{
if (i == -1)
return head;
node *p = head->next;
while (i--)
p = p->next;
return p;
}
Reason: This will walk the list uninhibited by validity checking for i iterations. Now imagine what this does on an empty list (in your case, that means head is non-null, but head->next is null) when passed anything besides -1: It will return NULL for i=0 and is outright undefined behavior for everything else.
NULL Pointer Dereference
void list::insert(int i, const string &din)
{
node *p = new node(din);
node *pp = findnode(i-1);
p->next = pp->next;
pp->next = p;
N++;
}
This assumes pp will never be null on return, and as we already discussed with the prior item, it most certainly can be when head is the sole node in your list, and is therefore "empty". This makes no attempt at checking pp for NULL prior to using it for dereferencing. This kid-gloves handling and the exceptions that have to be accounted for are directly related to maintaining a "sentinel" head node. The simplest way to fix it is to (a) Don't use sentinel nodes; use the universal sentinel value nullptr, and (b) check your return values before using them.
Ambiguous Reference: rank
card = rank[ rand()%13 ];
Reason: The standard library defines a special struct called std::rank used for determining the number of dimensions in a multi-dimension array. With the using namespace std; at the top of your code, the compiler is now forced to choose which one (the one in namespace std or the array you've defined prior to this code), and it cannot do so unequivocally. Thus it will not compile. Note: this is brought in by implicitly including <type_traits>, which is likely included by <string>, <iostream>, <iomanip> or any of a number of other nested includes. You can solve it a number of ways, including (but not limited to) a creative using clause, renaming the rank array to something that doesn't conflict, using a functional wrapper around a local static rank in the function etc.
Implicit conversion from signed to unsigned type (minor)
srand(seedvalue);
Reason: std::srand() takes an unsigned int parameter; you're passing a signed integer. Either static-cast to unsigned int or change the type of seedValue to unsigned int.
Invalid Code
list deck[4];
Reason: Class list does not have a default constructor. Recall the first item in this response. If you fix that, you will fix this as well.
And I didn't even run the code yet. I would strongly advise working on these issues, and give serious consideration to not using a "sentinel" node for your list head. Linked list code practically writes itself once you "know" a null head means the list is empty, a non-null head means it isn't.
I make no claims this is all the bugs. These were just ones I saw while reviewing the code, and all but one of them is significant.
EDIT Sample operator overload
Note: If you fix your linked list to use null as a head value when the list is empty (advised) this will need to change to simply start at head rather than head>next.
std::ostream& operator <<(std::ostream& os, const list& lst)
{
const node *p = lst.head ? lst.head->next : nullptr;
while (p)
{
os << p->data;
if ((p = p->next)) // note: assignment intentional
os << ',';
}
return os;
}

Need help with copy constructor for very basic implementation of singly linked lists

Last week, we created a program that manages sets of strings, using classes and vectors. I was able to complete this 100%. This week, we have to replace the vector we used to store strings in our class with simple singly linked lists.
The function basically allows users to declare sets of strings that are empty, and sets with only one element. In the main file, there is a vector whose elements are a struct that contain setName and strSet (class).
HERE IS MY PROBLEM: It deals with the copy constructor of the class. When I remove/comment out the copy constructor, I can declare as many empty or single sets as I want, and output their values without a problem. But I know I will obviously need the copy constructor for when I implement the rest of the program. When I leave the copy constructor in, I can declare one set, either single or empty, and output its value. But if I declare a 2nd set, and i try to output either of the first two sets, i get a Segmentation Fault. Moreover, if i try to declare more then 2 sets, I get a Segmentation Fault. Any help would be appreciated!!
Here is my code for a very basic implementation of everything:
Here is the setcalc.cpp: (main file)
#include <iostream>
#include <cctype>
#include <cstring>
#include <string>
#include "strset2.h"
using namespace std;
// Declares of structure to hold all the sets defined
struct setsOfStr {
string nameOfSet;
strSet stringSet;
};
// Checks if the set name inputted is unique
bool isSetNameUnique( vector<setsOfStr> strSetArr, string setName) {
for(unsigned int i = 0; i < strSetArr.size(); i++) {
if( strSetArr[i].nameOfSet == setName ) {
return false;
}
}
return true;
}
int main() {
char commandChoice;
// Declares a vector with our declared structure as the type
vector<setsOfStr> strSetVec;
string setName;
string singleEle;
// Sets a loop that will constantly ask for a command until 'q' is typed
while (1) {
cin >> commandChoice;
// declaring a set to be empty
if(commandChoice == 'd') {
cin >> setName;
// Check that the set name inputted is unique
if (isSetNameUnique(strSetVec, setName) == true) {
strSet emptyStrSet;
setsOfStr set1;
set1.nameOfSet = setName;
set1.stringSet = emptyStrSet;
strSetVec.push_back(set1);
}
else {
cerr << "ERROR: Re-declaration of set '" << setName << "'\n";
}
}
// declaring a set to be a singleton
else if(commandChoice == 's') {
cin >> setName;
cin >> singleEle;
// Check that the set name inputted is unique
if (isSetNameUnique(strSetVec, setName) == true) {
strSet singleStrSet(singleEle);
setsOfStr set2;
set2.nameOfSet = setName;
set2.stringSet = singleStrSet;
strSetVec.push_back(set2);
}
else {
cerr << "ERROR: Re-declaration of set '" << setName << "'\n";
}
}
// using the output function
else if(commandChoice == 'o') {
cin >> setName;
if(isSetNameUnique(strSetVec, setName) == false) {
// loop through until the set name is matched and call output on its strSet
for(unsigned int k = 0; k < strSetVec.size(); k++) {
if( strSetVec[k].nameOfSet == setName ) {
(strSetVec[k].stringSet).output();
}
}
}
else {
cerr << "ERROR: No such set '" << setName << "'\n";
}
}
// quitting
else if(commandChoice == 'q') {
break;
}
else {
cerr << "ERROR: Ignoring bad command: '" << commandChoice << "'\n";
}
}
return 0;
}
Here is the strSet2.h:
#ifndef _STRSET_
#define _STRSET_
#include <iostream>
#include <vector>
#include <string>
struct node {
std::string s1;
node * next;
};
class strSet {
private:
node * first;
public:
strSet (); // Create empty set
strSet (std::string s); // Create singleton set
strSet (const strSet &copy); // Copy constructor
// will implement destructor and overloaded assignment operator later
void output() const;
}; // End of strSet class
#endif // _STRSET_
And here is the strSet2.cpp (implementation of class)
#include <iostream>
#include <vector>
#include <string>
#include "strset2.h"
using namespace std;
strSet::strSet() {
first = NULL;
}
strSet::strSet(string s) {
node *temp;
temp = new node;
temp->s1 = s;
temp->next = NULL;
first = temp;
}
strSet::strSet(const strSet& copy) {
cout << "copy-cst\n";
node *n = copy.first;
node *prev = NULL;
while (n) {
node *newNode = new node;
newNode->s1 = n->s1;
newNode->next = NULL;
if (prev) {
prev->next = newNode;
}
else {
first = newNode;
}
prev = newNode;
n = n->next;
}
}
void strSet::output() const {
if(first == NULL) {
cout << "Empty set\n";
}
else {
node *temp;
temp = first;
while(1) {
cout << temp->s1 << endl;
if(temp->next == NULL) break;
temp = temp->next;
}
}
}
The C++ standard states that types used in a standard container (such as std::vector) must be copy constructable AND assignable.
Since you have not implemented a custom assignment operator on the class strSet, the compiler will generate one for you that does a simple memberwise copy. In your case, this means the 'first' pointer will be copied directly. Obviously this means two objects now 'own' the nodes in the set, and you will get a crash when it is freed twice.
Some tips:
Implement a custom assignment operator that does the same thing as your copy constructor
Read up on passing objects by reference, and by const reference where possible. You are doing a lot of unnecessary copying of your containers and strings when you pass by value.
e.g.
bool isSetNameUnique(const vector& strSetArr, const string& setName)
Good luck :)
this looks a bit peculiar:
strSet::strSet(string s) {
node *temp;
temp = new node;
temp->s1 = s;
temp->next = NULL;
first = temp;
}
what if 'first' is pointing to something already? You are then effectively killing the previous list and causing a mem leak.
Your strSet copy constructor does not assign the member first when its argument is empty. This causes Undefined Behavior.
Also, the strSet assignment operator (operator=) that was shown before the edit was definitely wrong; and it really is not a good idea to define a copy constructor but allow the destructor and assignment operator to be implicitly defined by the compiler. See the Rule of Three.
One common way to implement the Big Three when they need to do considerable management (like in this case) looks something like:
class strSet {
private:
void cleanup();
void create_from(const node* n);
// ...
};
strSet::~strSet() { cleanup(); }
strSet::strSet(const strSet& copy) : first(NULL) { create_from(copy.first); }
strSet& strSet::operator=(const strSet& rtSide) {
if (this != &rtSide) {
cleanup(); // trash old contents of *this
create_from(rtSide.first); // clone contents of rtSide
}
return *this;
}