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

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;
}

Related

Linked-List only remembers most recent added object

I have an assignment that requires me to add objects into a linked list. The objects in question are Shapes.
My problem is that I can add objects to the list, but when I try to print them out, only the last added object is printed, the rest are just trash values.
My code looks like this:
Source.cpp:
#include "ShapeList.h"
#include <iostream>
using namespace std;
int main()
{
ShapeList list;
list.add(Rectangle(0,0,2,5));
list.print();
}
I am not allowed to change this code. For example, I am not allowed to send a pointer to the new rectangle, I'm supposed to "deep-copy" it. (I hope I'm using that word right.)
My ShapeList.h looks like this:
#ifndef SHAPELIST_H
#define SHAPELIST_H
#include "Shape.h"
#include "Rectangle.h"
class ShapeList
{
private:
Shape *conductor; //this will point to each node as it traverses the list
Shape *root; //the unchanging first node
public:
ShapeList();
void print();
void add(const Shape &s);
};
#endif
and the header looks like:
#include "ShapeList.h"
#include <iostream>
using namespace std;
ShapeList::ShapeList()
{
cout << "ShapeList created" << endl;
root = new Shape; //now root points to a node class
root->next = 0; //the node root points to has its next pointer set to equal a null pointer
conductor = root; //the conductor points to the first node
}
void ShapeList::add(const Shape &s)
{
cout << "Shapelist's add function called" << endl;
conductor->next = new Shape; //creates node at the end of the list
conductor = conductor->next; //goes to next node
Shape *pShape = s.clone(); //get a pointer to s
conductor->current = pShape; //points current to pShape point
conductor->next = 0; //prevents loops from going out of bounds
}
void ShapeList::print()
{
conductor = root; //the conductor points to the start of the linked list
if(conductor != 0)
{
while(conductor->next != 0)
{
conductor = conductor->next;
cout << conductor->current->width << endl;
}
//cout << conductor->current->width << endl;
}
}
The clone-function is overloaded in all shapes, in this case it's the rectangle's:
Rectangle * Rectangle::clone() const
{
cout << "Rectangle's clone function called" << endl;
Rectangle copiedRect(this);
Rectangle * pCopiedRect = &copiedRect;
return pCopiedRect;
}
Rectangle::Rectangle(const Rectangle *ref)
{
cout << "Rectangle's copy constructor called" << endl;
this->x = ref->x;
this->y = ref->y;
this->width = ref->width;
this->height = ref->height;
}
I know it's alot to read, and I'm sorry. I can remove stuff if it's not needed. I can also add more if you would like.
I have read Alex Allain's tutorial* about linked lists, and a couple of other articles. If anyone has another article, or something like that, to suggest I'm all ears.
http://www.cprogramming.com/tutorial/c/lesson15.html
Rectangle::clone() is invoking undefined behavior. You're returning the address of an automatic variable copiedRect, which falls of scope as soon as the function terminates.
Try this:
Rectangle * Rectangle::clone() const
{
cout << "Rectangle's clone function called" << endl;
return new Rectangle(*this);
}
And your copy-ctor should not even need to be implemented. All the members of Rectangle are trivially copyable. The default should work fine.
Note: I didn't really take the time to dissect your list insertion code, but the above is definitely a problem that needs to be addressed.

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;
}

Class Tree vs Structure TreeNode

I have two separate programs, both dealing with evaluating expression trees with infix and postfix. One is based on structures while the other is based on classes. Now I am stuck on the part of my assignment(for the class version) that says to:
"Finish the implementation of the createExpressionTree() method"
and that
"Its very similar to your previous implementation, except that you will be using instances of the "class Tree" instead of instances of the "structure TreeNode"."
There is more to the both than what is below, but I think you can get the gist of things, so what I am asking is: How similar is the class implementation to the structure one? And can I just copy and paste the old code in that is shown below and make minor adjustments? I have been trying but am have issues with accessing private members and such.
So here is the class tree and my new version of createExpressionTree that is supposed to go with it
#ifndef TREE_H
#define TREE_H
#include <vector>
#include <stack>
#include <sstream>
#include <map>
# define TYPE_NUMBER 0
# define TYPE_VARIABLE 1
# define TYPE_OPERATOR 2
class Tree
{
public:
Tree(std::string input,Tree *leftSubTree=NULL,Tree *rightSubTree=NULL);
Tree(const Tree &inTree); //COPY CONSTRUCTOR
~Tree(); //DESTRUCTOR
int evaluate(std::map< std::string, int > ipMap); //EVALUATE THE EXPRESSION
void postOrderPrint();
void inOrderPrint();
private:
Tree *leftPtr;
std::string Op;
Tree *rightPtr;
int NodeType;
};
code associated with tree class
Tree::Tree(std::string input,Tree *leftSubTree,Tree *rightSubTree){
Op = input;
leftPtr = leftSubTree;
rightPtr = rightSubTree;
int num;
if (input == "+"|input == "-"|input == "*"|input == "/")
NodeType = TYPE_OPERATOR;
else if(std::istringstream(Op)>>num)
NodeType = TYPE_NUMBER;
else
NodeType = TYPE_VARIABLE;
}
// copy constructor
Tree::Tree(const Tree &inTree){
Op = inTree.Op;
NodeType = inTree.NodeType;
if (inTree.leftPtr == NULL){
leftPtr = NULL;
}
else {
leftPtr = new Tree(*(inTree.leftPtr));
}
if (inTree.rightPtr == NULL){
rightPtr = NULL;
}
else {
rightPtr = new Tree(*(inTree.rightPtr));
}
}
// tree destructor
Tree::~Tree(){
std::cout << "Tree destructor called" << std::endl;
if (leftPtr != NULL) {
delete(leftPtr);
leftPtr = NULL;
}
if (rightPtr != NULL) {
delete(rightPtr);
rightPtr = NULL;
}
}
#endif
New createExpressionTree that I would love some help with:
void arithmetic_expression::createExpressionTree(std::vector<std::string> expression)
{
std::stack <Tree> localStack;
std::string Op;
//Very similar to old implementation
}
And here is the previous implementation of the structure treeNode and the previous createExpressionTree that is completed
struct treeNode {
treeNode *leftPtr; /* pointer to left subtree */
std::string Op; /* integer data value */
treeNode *rightPtr; /* pointer to right subtree */
};
typedef struct treeNode TreeNode;
typedef TreeNode * TreeNodePtr;
previous createExpressionTree
void arithmetic_expression::createExpressionTree(std::vector<std::string> expression)
{
std::stack <TreeNodePtr> localStack;
std::string Op;
TreeNodePtr ptr;
for(int i=0; i<expression.size();i++)
{
Op = expression[i];
ptr = createNewTreeNode(Op);
if(char_is_operator(Op))
{
// adding element to right tree
if (localStack.empty())
{
std::cout<< "Invalid expression: tree not created " << std::endl;
topPtr = NULL;
return;
}
else
{
ptr->rightPtr = localStack.top();
localStack.pop();
}
// adding element to left tree
if (localStack.empty()) {
std::cout<< "Invalid expression: tree not created " << std::endl;
topPtr = NULL;
return;
}
else
{
ptr->leftPtr = localStack.top();
localStack.pop();
}
}
// pushing element to stack
localStack.push(ptr);
}
if (localStack.empty()) {
std::cout<< "Invalid expression: tree not created " << std::endl;
topPtr = NULL;
}
else
{
topPtr = localStack.top();
localStack.pop();
if (!localStack.empty()) {
std::cout<< "Invalid expression: tree not created " << std::endl;
topPtr = NULL;
}
}
}
I don't think there would be any difference between the class and structure version(the only difference between structs and classes is that classes have data private by default).
If he only wants you to change the word "struct" to "class", then you can access leftPtr/rightPtr by using the access modifier "public"
Example:
struct blah
{
int boringNumbers;
string boringStrings;
};
Is the same as:
class blah2
{
public:
int boringNumbers;
string boringStrings;
};
However, he might also want you to design your application to move more code into the class.
That would at the very least include moving functions such as char_is_operator into the class.
I doubt it, but he/she might also want you to create a hierarchy of nodes so you don't need to store data as generic strings.

Destructor, doesn't delete my object

I have big problem- namely my destructor doesn't delete object, in my code which i will paste underneath in main when i call l3.~list(); it removes only singly linked list(which is good), but it doesn't remove char* name, even though I am stating in my destructor delete [] name;. Any ideas whats wrong?
Here is the code;
#include <iostream>
#include <cstdlib>
#include <string>
using namespace std;
class list{
struct lista
{
int num;
char* word;
lista* next;
};
lista* head;
char* name;
public:
list(char* name1){head=NULL;name=new char[strlen(name1)+1];strcpy(name,name1);}
char getChar(int key, int index);
void setChar(int key, int index, char c);
void insert(int number,char* txt);
void remove(int number);
void print();
list(const list &o);
list& operator=(const list &x);
~list();
};
void list::insert(int number,char* txt){
lista* ptr,*tmp;
ptr=head;
lista* newlista=new lista;
newlista->num=number;
newlista->next=NULL;
newlista->word= new char[strlen(txt)+1];
strcpy(newlista->word,txt);
if(head==NULL){
head=newlista;
newlista->next=NULL;
}
else while(ptr!=NULL){
if(strcmp(txt,ptr->word)>=0){
if(ptr->next!=NULL && strcmp(txt,ptr->next->word)<=0)
{
tmp=ptr->next;
ptr->next=newlista;
newlista->next=tmp;
break;
}
else if(ptr->next!=NULL && strcmp(txt,ptr->next->word)>0)
ptr=ptr->next;
else
{
//next is empty
ptr->next=newlista;
break;
}
}
else{
//txt mniejszy niz w 1szym elemencie
newlista->next=head;
head=newlista;
break;
}
}
return;
}
void list::print(){
cout<<name<<";"<<endl;
lista *druk;
druk=head;
while(druk!=NULL){
cout<<"txt: "<<druk->word<<" | "<<"num: "<<druk->num<<endl;
druk=druk->next;
}
cout<<endl;
return;
}
void list::remove(int number){
if(head==NULL)
return;
if(head->num==number){
lista* ptr=head;
head=head->next;
delete [] ptr->word;
delete ptr;
return;
}
lista* ptr=head;
while(ptr->next!=NULL && ptr->next->num!=number)
ptr=ptr->next;
if(ptr->next==NULL){
cout<<number<<" element not found"<<endl;
return;
}
lista* todelete=ptr->next;
ptr->next=todelete->next;
delete [] todelete->word;
delete todelete;
return;
}
list::list(const list &o)
{
lista *xtr = o.head;
head=NULL;// bez tego nie dziaƂa
lista *etr=head;// nastawic etr na head?
while (xtr)
{
lista* ntr = new lista;
if (!ntr)
{
cerr << "list::CopyConstructor: Allocation memory failure!";
cerr << endl;
break;
}
ntr->num = xtr->num;
ntr->word= new char[strlen(xtr->word)+1];
strcpy(ntr->word,xtr->word);
ntr->next = NULL;
if (head)
etr->next = ntr;
else
head = ntr;
etr = ntr; // keep track of the last element in *this
xtr = xtr->next;
}
name = new char[strlen(o.name)+5];
strcpy(name,o.name);
strcat(name,"Copy");
}
list& list::operator=(const list &x)
{
if(this==&x)
return *this;
lista *etr=head;
while(etr) // removing list from this
{
etr=etr->next;
delete head;
head=etr;
}
lista *xtr=x.head;
while(xtr)
{
int copied=xtr->num;
lista *ntr= new lista;
ntr->word=new char[strlen(xtr->word)+1];
if (!ntr)
{
cerr << "list::operator=: Allocation memory failure!" << endl;
break;
}
ntr->num=copied;
strcpy(ntr->word,xtr->word);
ntr->next=NULL;
if (!head)
head = ntr;
else
etr->next = ntr;
etr = ntr; // keep track of the last element in *this
xtr = xtr->next;
}
char *name=new char[strlen(x.name)+1];
strcpy(name,x.name);
return *this;
}
list::~list()
{
cout<<"Object with name:"<<name<<" destroyed!"<<endl;
delete [] name;
lista *dtr=head;
while(dtr) // removing lista from this
{
dtr=dtr->next;
delete [] head->word;
delete head;
head=dtr;
}
}
void f();
void f(){
list o("xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx");
o.insert(4,"kazio");
o.insert(100,"312jh31io2");
o.insert(34,"kz31231azio");
o.insert(1020,"123213312jh31io2");
o.insert(213123,"z3213io");
o.insert(1100,"zdrf312jh31io2");
o.print();
}
int main(){
list l1("lista1");
l1.insert(5,"Endian");
l1.insert(7,"Endianness");
l1.insert(100,"Hexediting");
l1.insert(34,".mil");
l1.print();
list l2(l1); // usage of CC - the same as list l2=l1;
l2.print();
l2.remove(5);
l2.print();
l1.print();
list l3("asajnment");
l3=l2=l1;
l3.print();
l2.print();
f();
l3.print();
l3.~list(); // here i use destructor on l3
l3.print(); // l3 is being printed with weird name, even though it should be destroyed
getchar();
return 0;
}
Calling any method after invoking destructor results in undefined behaviour - it may or may nor work and it can produce strange results.
Also, you are not supposed to call the destructor directly:
When the object is allocated on stack, it is destroyed automatically when the scope ends. (Scope is the thing between braces {})
When the object is allocated on heap, using new, it should be destroyed using delete.
C++ destructors are not like deallocation functions as you might write in C. They're better: in the RAII idiom, you have destruction of your objects scheduled to the very moment they exit scope. That means you usually don't have to care for freeing resources at all: just wait until the object is no longer needed (because it can't be accessed), at that points it gets automatically removed (which includes calling the destructor, yes, and that's the only way in which it may be called safely). So well-written C++ is in many ways as good as garbage-collected languages, but without some of their drawbacks.
The easiest way to get the benefits of RAII is to use standard containers and smart pointers. In your case, replace lista* next with std::unique_ptr<lista> next and char* word with std::string word, and all is fine without the need to define a destructor at all.
There is so much wrong with this code that I don't know where to start...
use std::string
use a std::map to associate int values with the strings. This will pretty much already do what you want.
don't call the destructor for anything that was not new'd. To delete something use delete/delete[] and don't call the destructor directly. If you do use new, use the RAII idiom using managing objects such as std::unique_ptr or std::shared_ptr to avoid having to manually call delete/delete[] and to write exception safe code
Here is a somewhat improved version. Notice that there is not a single call to new/delete.
#include <iostream>
#include <string>
#include <map>
#include <cstdio>
class list
{
public:
explicit
list( std::string n ) : name( n ) {}
~list() { std::cout << "~list:" << name << std::endl; }
void list::insert(int number, std::string const& txt ){
items.insert( std::make_pair(number,txt));
}
void list::remove(int number){
items.erase( number );
}
void print( ){
std::cout << name << ";" << std::endl;
for( Items::const_iterator it = items.begin(), end = items.end(); it != end; ++it )
{
std::cout << "num: " << it->first << " | " << "txt: " << it->second << std::endl;
}
std::cout << std::endl;
}
private:
typedef std::map<int,std::string> Items;
Items items;
std::string name;
};
int main()
{
list l1( "lista1" );
l1.insert( 5, "Endian");
l1.insert( 7, "Endianness");
l1.insert( 100, "Hexediting");
l1.insert( 34, ".mil");
// extra scope so the destructor of l2 is called before call to getchar
{
list l2( l1 );
l2.remove( 5 );
l2.print();
}
l1.print();
getchar();
return 0;
}
One way of making sure that your members are not being accessed by mistake after destruction is to set all pointers to NULL after deleting them.
That way, you're assured that nobody can get to your sensitive data afterwards, because you're no longer pointing to it. And you can call the destructor again without bad side effects, because calling delete on a NULL pointer is allowed and does nothing.
If you print the memory state of your object after deleting it, you will see the value stay until you don't alloc a new object. The memory allocated for your program can only go bigger. When you delete data, they are not set to '0', just marked as free for the next alloc object.
EDIT: I mean if you create a new object with uninitialized values just after free, he can get back the old value stored in memory.

C++ HW Help using stack

I am a little stuck on how to use the stack and why I would even use stack in the code I am writing. The assingment says to write a program that checks if the user input is well-Iformed or not. It is a simple prgram that has three different selections the use can choose from. 1. basic brackets () 2. standard brackets ()[]{} and 3. User-definded brackets. The only thing the main program is suppose to do is to check if the users input is well-formed or not and display only that message on the screen.
I have a StackLS.cpp and a Stack.h file I am using along with my main.cpp. I will paste a sample code below from each.
StackLS.h
typedef int elemType; // flexible data type
class StackLS
{
private:
// inner class node
class Node
{
public:
elemType data; // data portion
Node *next; // link to the seccessor
}; // end Node
// data members
Node *topItem; // pointer to the top element of this stack
// utilities
public:
// constructors
StackLS(void); // default constructor
StackLS(const StackLS& aStack); // copy constructor
// observers
bool isEmpty(void) const;
// returns true if this stack is empty
// false otherwise
bool isFull(void) const;
// returns true if this stack is full
// false otherwise
elemType top(void) const;
// precondition: this stack is not empty
// returns top element in this stack
// transformers
void push(const elemType& item);
// precondition: this stack is not full
// adds item to this stack
void pop(void);
// removes top element from this stack if exist
// remains empty otherwise
void makeEmpty(void);
// makes this stack empty
// destructor
~StackLS(void);
}; // end StackLS
StackLS.cpp
// constructors
StackLS::StackLS(void)
// default constructor
{
topItem = 0;
} // end default constructor
StackLS::StackLS(const StackLS& aStack)
// copy constructor
{
} // end copy constructor
// observers
bool StackLS::isEmpty(void) const
// returns true if this stack is empty
// false otherwise
{
return topItem == 0;
} // end isEmpty
bool StackLS::isFull(void) const
// returns true if this stack is full
// false otherwise
{
return false;
} // end isFull
elemType StackLS::top(void) const
// precondition: this stack is not empty
// returns top element in this stack
{
// return (*topItem).data;
return topItem->data;
} // end top
// transformers
void StackLS::push(const elemType& item)
// precondition: this stack is not full
// adds item to this stack
{
Node *newNode = new Node;
newNode->data = item;
newNode->next = topItem;
topItem = newNode;
} // end push
void StackLS::pop(void)
// removes top element from this stack if exist
// remains empty otherwise
{
if (topItem != 0)
{
Node *temp = topItem;
topItem = topItem->next;
delete temp;
}
} // end pop
void StackLS::makeEmpty(void)
// makes this stack empty
{
while (topItem != 0)
{
Node *temp = topItem;
topItem = topItem->next;
delete temp;
}
} // end makeEmpty
// destructor
StackLS::~StackLS(void)
{
//while (!isEmpty())
// pop();
while (topItem != 0)
{
Node *temp = topItem;
topItem = topItem->next;
delete temp;
}
} // end destructor
Here is the main.cpp that I have so far.
main.cpp
#include <iostream>
#include <string>
#include "StackLS.h"
using namespace std;
do {
int main()
{
char answer;
char n;
StackLS stack;
cout << " ********** MENU ********** " << endl;
cout << " 1. Basic Brackets () " << endl;
cout << " 2. Standard Brackets ()[]{} " << endl;
cout << " 3. User-Defined brackets " << endl;
cout << " Please enter your choice: " << endl;
switch (choice){
case 1:
cout << "Current Setting: () " << endl;
cout << "Enter your expression followed by a ; : " << endl;
do {
cin >> answer;
while (answer != ;)
}
} // end main
}
while (choice != 'n' || 'N')
Again I am wondering how I would use the stack I have shown you in this program (main.cpp). I am a little confused on why I would use stack and why. Any help is appreciated. Thanks. The main.cpp may not be right but again I am learning and that is why I am here to learn more. Thanks
When you see an opening brace, you push it onto the stack. When you see a closing brace, you make sure it is the counterpart of the brace on top of the stack, then pop it off. When your input is done, you make sure the stack is empty.