Class Tree vs Structure TreeNode - c++

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.

Related

C Creating a binary tree based on a sequence

I need help adjusting the createTree function.
Which accepts a string and after that character by character traverses it, creating a binary tree based on it
If it encounters the character 0, it recursively creates two sub-branches.
If it encounters another character, it saves it in the leaf node.
For the string in the example, I need to make a tree as in the picture, but the function does not work properly for me. Thank you in advance for your advice.
int x = 0;
Node* createTree(string str, int si, int ei)
{
if (si > ei)
return NULL;
Node *root = new Node((str[si] - '0'));
if(str[si] != '0')
{
x++;
root->m_Data = (str[si] - '0');
return root;
}
if(str[si]=='0')
{
x++;
root->m_Left = createTree(str,x,ei);
root->m_Right = createTree(str,x,ei);
}
return root;
}
int main ()
{
string str = "050067089";
Node *node = createTree(str,0,str.length());
printPreorder(node);
return 0;
}
The problem can quite easily be broken down into small steps (what you partly did in your question).
Start iterating at the first character
Create the root node
If the current character is non-zero, set the value of this node to this character
If current character is a zero, set this node to zero, create a left and a right node and get back to step 3 for every one of them. (That's the recursive part.)
Below is my implementation of this algorithm.
First, a little bit of setting up:
#include <iostream>
#include <string>
#include <memory>
struct Node;
// Iterator to a constant character, NOT a constant iterator
using StrConstIt = std::string::const_iterator;
using UniqueNode = std::unique_ptr<Node>;
struct Node
{
int value;
UniqueNode p_left;
UniqueNode p_right;
Node(int value)
: value(value) {}
Node(int value, UniqueNode p_left, UniqueNode p_right)
: value(value), p_left(std::move(p_left)), p_right(std::move(p_right)) {}
};
As you can see, I'm using std::unique_ptr for managing memory. This way, you don't have to worry about manually deallocating memory. Using smart pointers is often considered the more "modern" approach, and they should virtually always be preferred over raw pointers.
UniqueNode p_createNodeAndUpdateIterator(StrConstIt& it, StrConstIt stringEnd)
{
if (it >= stringEnd)
return nullptr;
UniqueNode node;
if (*it == '0')
// Create node with appropriate value
// Create branches and increment iterator
node = std::make_unique<Node>(
0,
p_createNodeAndUpdateIterator(++it, stringEnd),
p_createNodeAndUpdateIterator(it, stringEnd)
);
else
{
// Create leaf node with appropriate value
node = std::make_unique<Node>(*it - '0');
// Increment iterator
++it;
}
return node;
}
UniqueNode p_createTree(StrConstIt begin, StrConstIt end)
{
return p_createNodeAndUpdateIterator(begin, end);
}
The first function takes a reference to the iterator to the next character it should process. That is because you can't know how much characters a branch will have in its leaf nodes beforehand. Therefore, as the function's name suggests, it will update the iterator with the processing of each character.
I'm using iterators instead of a string and indices. They are clearer and easier to work with in my opinion — changing it back should be fairly easy anyway.
The second function is basically syntactic sugar: it is just there so that you don't have to pass an lvalue as the first argument.
You can then just call p_createTree with:
int main()
{
std::string str = "050067089";
UniqueNode p_root = p_createTree(str.begin(), str.end());
return 0;
}
I also wrote a function to print out the tree's nodes for debugging:
void printTree(const UniqueNode& p_root, int indentation = 0)
{
// Print the value of the node
for (int i(0); i < indentation; ++i)
std::cout << "| ";
std::cout << p_root->value << '\n';
// Do nothing more in case of a leaf node
if (!p_root->p_left.get() && !p_root->p_right.get())
;
// Otherwise, print a blank line for empty children
else
{
if (p_root->p_left.get())
printTree(p_root->p_left, indentation + 1);
else
std::cout << '\n';
if (p_root->p_right.get())
printTree(p_root->p_right, indentation + 1);
else
std::cout << '\n';
}
}
Assuming that the code which is not included in your question is correct, there is only one issue that could pose a problem if more than one tree is built. The problem is that x is a global variable which your functions change as a side-effect. But if that x is not reset before creating another tree, things will go wrong.
It is better to make x a local variable, and pass it by reference.
A minor thing: don't use NULL but nullptr.
Below your code with that change and the class definition included. I also include a printSideways function, which makes it easier to see that the tree has the expected shape:
#include <iostream>
using namespace std;
class Node {
public:
int m_Data;
Node* m_Left = nullptr;
Node* m_Right = nullptr;
Node(int v) : m_Data(v) {}
};
// Instead of si, accept x by reference:
Node* createTree(string str, int &x, int ei)
{
if (x >= ei)
return nullptr;
Node *root = new Node((str[x] - '0'));
if(str[x] != '0')
{
root->m_Data = (str[x] - '0');
x++;
return root;
}
if(str[x]=='0')
{
x++;
root->m_Left = createTree(str,x,ei);
root->m_Right = createTree(str,x,ei);
}
return root;
}
// Overload with a wrapper that defines x
Node* createTree(string str)
{
int x = 0;
return createTree(str, x, str.length());
}
// Utility function to visualise the tree with the root at the left
void printSideways(Node *node, string tab) {
if (node == nullptr) return;
printSideways(node->m_Right, tab + " ");
cout << tab << node->m_Data << "\n";
printSideways(node->m_Left, tab + " ");
}
// Wrapper for above function
void printSideways(Node *node) {
printSideways(node, "");
}
int main ()
{
string str = "050067089";
Node *node = createTree(str);
printSideways(node);
return 0;
}
So, as you see, nothing much was altered. Just si was replaced with x, which is passed around by reference, and x is defined locally in a wrapper function.
Here is the output:
9
0
8
0
7
0
6
0
5

Hash table implementation in C++

I am trying the following code for Hash table implementation in C++. The program compiles and accepts input and then a popup appears saying " the project has stopped working and windows is checking for a solution to the problem. I feel the program is going in the infinite loop somewhere. Can anyone spot the mistake?? Please help!
#include <iostream>
#include <stdlib.h>
#include <string>
#include <sstream>
using namespace std;
/* Definitions as shown */
typedef struct CellType* Position;
typedef int ElementType;
struct CellType{
ElementType value;
Position next;
};
/* *** Implements a List ADT with necessary functions.
You may make use of these functions (need not use all) to implement your HashTable ADT */
class List{
private:
Position listHead;
int count;
public:
//Initializes the number of nodes in the list
void setCount(int num){
count = num;
}
//Creates an empty list
void makeEmptyList(){
listHead = new CellType;
listHead->next = NULL;
}
//Inserts an element after Position p
int insertList(ElementType data, Position p){
Position temp;
temp = p->next;
p->next = new CellType;
p->next->next = temp;
p->next->value = data;
return ++count;
}
//Returns pointer to the last node
Position end(){
Position p;
p = listHead;
while (p->next != NULL){
p = p->next;
}
return p;
}
//Returns number of elements in the list
int getCount(){
return count;
}
};
class HashTable{
private:
List bucket[10];
int bucketIndex;
int numElemBucket;
Position posInsert;
string collision;
bool reportCol; //Helps to print a NO for no collisions
public:
HashTable(){ //constructor
int i;
for (i=0;i<10;i++){
bucket[i].setCount(0);
}
collision = "";
reportCol = false;
}
int insert(int data){
bucketIndex=data%10;
int col;
if(posInsert->next==NULL)
bucket[bucketIndex].insertList(data,posInsert);
else { while(posInsert->next != NULL){
posInsert=posInsert->next;
}
bucket[bucketIndex].insertList(data,posInsert);
reportCol=true;}
if (reportCol==true) col=1;
else col=0;
numElemBucket++;
return col ;
/*code to insert data into
hash table and report collision*/
}
void listCollision(int pos){
cout<< "("<< pos<< "," << bucketIndex << "," << numElemBucket << ")"; /*codeto generate a properly formatted
string to report multiple collisions*/
}
void printCollision();
};
int main(){
HashTable ht;
int i, data;
for (i=0;i<10;i++){
cin>>data;
int abc= ht.insert(data);
if(abc==1){
ht.listCollision(i);/* code to call insert function of HashTable ADT and if there is a collision, use listCollision to generate the list of collisions*/
}
//Prints the concatenated collision list
ht.printCollision();
}}
void HashTable::printCollision(){
if (reportCol == false)
cout <<"NO";
else
cout<<collision;
}
The output of the program is the point where there is a collision in the hash table, thecorresponding bucket number and the number of elements in that bucket.
After trying dubbuging, I come to know that, while calling a constructor you are not emptying the bucket[bucketIndex].
So your Hash Table constructor should be as follow:
HashTable(){ //constructor
int i;
for (i=0;i<10;i++){
bucket[i].setCount(0);
bucket[i].makeEmptyList(); //here we clear for first use
}
collision = "";
reportCol = false;
}
//Creates an empty list
void makeEmptyList(){
listHead = new CellType;
listHead->next = NULL;
}
what you can do is you can get posInsert using
bucket[bucketIndex].end()
so that posInsert-> is defined
and there is no need to
while(posInsert->next != NULL){
posInsert=posInsert->next;
because end() function is doing just that so use end() function

Segfault in recursive function

I'm getting a segfault when I run this code and I'm not sure why. Commenting out a particular line (marked below) removes the segfault, which led me to believe that the recursive use of the iterator "i" may have been causing trouble, but even after changing it to a pointer I get a segfault.
void executeCommands(string inputstream, linklist<linklist<transform> > trsMetastack)
{
int * i=new int;
(*i) = 0;
while((*i)<inputstream.length())
{
string command = getCommand((*i),inputstream);
string cmd = getArguments(command,0);
//cout << getArguments(command,0) << " " << endl;
if (cmd=="translate")
{
transform trs;
trs.type=1;
trs.arguments[0]=getValue(getArguments(command,2));
trs.arguments[1]=getValue(getArguments(command,3));
((trsMetastack.top)->value).push(trs);
executeCommands(getArguments(command,1),trsMetastack);
}
if (cmd=="group")
{
//make a NEW TRANSFORMS STACK, set CURRENT stack to that one
linklist<transform> transformStack;
trsMetastack.push(transformStack);
//cout << "|" << getAllArguments(command) << "|" << endl;
executeCommands(getAllArguments(command),trsMetastack); // COMMENTING THIS LINE OUT removes the segfault
}
if (cmd=="line")
{ //POP transforms off of the whole stack/metastack conglomeration and apply them.
while ((trsMetastack.isEmpty())==0)
{
while ((((trsMetastack.top)->value).isEmpty())==0) //this pops a single _stack_ in the metastack
{ transform tBA = ((trsMetastack.top)->value).pop();
cout << tBA.type << tBA.arguments[0] << tBA.arguments[1];
}
trsMetastack.pop();
}
}
"Metastack" is a linked list of linked lists that I have to send to the function during recursion, declared as such:
linklist<transform> transformStack;
linklist<linklist<transform> > trsMetastack;
trsMetastack.push(transformStack);
executeCommands(stdinstring,trsMetastack);
The "Getallarguments" function is just meant to extract a majority of a string given it, like so:
string getAllArguments(string expr) // Gets the whole string of arguments
{
expr = expr.replace(0,1," ");
int space = expr.find_first_of(" ",1);
return expr.substr(space+1,expr.length()-space-1);
}
And here is the linked list class definition.
template <class dataclass>
struct linkm {
dataclass value; //transform object, point object, string... you name it
linkm *next;
};
template <class dataclass>
class linklist
{
public:
linklist()
{top = NULL;}
~linklist()
{}
void push(dataclass num)
{
cout << "pushed";
linkm<dataclass> *temp = new linkm<dataclass>;
temp->value = num;
temp->next = top;
top = temp;
}
dataclass pop()
{
cout << "pop"<< endl;
//if (top == NULL) {return dataclass obj;}
linkm<dataclass> * temp;
temp = top;
dataclass value;
value = temp->value;
top = temp->next;
delete temp;
return value;
}
bool isEmpty()
{
if (top == NULL)
return 1;
return 0;
}
// private:
linkm<dataclass> *top;
};
Thanks for taking the time to read this. I know the problem is vague but I just spent the last hour trying to debug this with gdb, I honestly dunno what it could be.
It could be anything, but my wild guess is, ironically: stack overflow.
You might want to try passing your data structures around as references, e.g.:
void executeCommands(string &inputstream, linklist<linklist<transform> > &trsMetastack)
But as Vlad has pointed out, you might want to get familiar with gdb.

Strcmp Error in Binary Search Tree Insertion (Recursion)?

i'm trying to implement some functions that allow me to add "Books" to a binary search tree for the "Student" class, but I'm getting a strange error:
msvcr100d.dll!strcmp(unsigned char * str1, unsigned char * str2) Line 83 Asm
The program is entirely in C/C++, so I'm not sure why its returning an assembly language error? My first thought is something is wrong with my use of strcmp, and the Call Stack shows Line 188 as the last executed statement (before the above error), which means I'm probably messing up my recursion somewhere. I am calling the insertBook() function of "Student", so here is my "Student" class. Any help? Thanks.
class Student : public Personnel { //inherit from Personnel
public:
Book *bookTree;
Book* searchBookTree(Book *bookNode, char *title) {
if ((strcmp(title, bookNode->title)) < 0) //***LINE 188
return searchBookTree(bookNode->left, title);
else if ((strcmp(title, bookNode->title)) > 0)
return searchBookTree(bookNode->right, title);
else
return bookNode;
}
void insertBook(Book *node) {
Book *newBook, *parent;
newBook = node;
newBook->left = NULL;
newBook->right = NULL;
if (bookTree == NULL) { //if bookTree is empty
bookTree = newBook;
}
else {
parent = searchBookTree(bookTree, newBook->title);
newBook->left = parent->left;
newBook->right = parent->right;
}
}
void printBooks(Book *top) {
Book *root = top;
if (root != NULL) {
printBooks(root->left);
cout << "BOOK LIST" << endl;
cout << "Title:\t\t" << root->title << endl;
cout << "URL:\t\t" << root->url << endl;
printBooks(root->right);
}
}
void display() {
Personnel::display();
cout << "STUDENT" << endl;
cout << "Level:\t\t" << getLevel() << endl;
printBooks(bookTree); cout << endl;
}
Student(char *cName, char *cBirthday, char *cAddress, char *cPhone, char *cEmail, level gradeLevel)
: Personnel(cName, cBirthday, cAddress, cPhone, cEmail)
{
bookTree = NULL;
setLevel(gradeLevel);
}
};
Book* searchBookTree(Book *bookNode, char *title) {
if ((strcmp(title, bookNode->title)) < 0) //***LINE 188
// What happens if bookNode->left == NULL ???
return searchBookTree(bookNode->left, title);
else if ((strcmp(title, bookNode->title)) > 0)
// What happens if bookNode->right== NULL ???
return searchBookTree(bookNode->right, title);
else
return bookNode;
}
you'll need a termination point in your search function. At the top, I'd first check if bookNode == NULL.
Your recursive search an important termination test missing! At some point, you hit the bottom of the tree without finding the item. And so your search function is called with a null pointer for the tree node! The problem is not in strcmp, but in the null pointer in one of the argument expressions.
You have only considered the case when the item exists in the tree and is eventually found, neglecting the not-found case.
Programmers are not to be measured by their ingenuity and their logic but by the completeness of their case analysis.
Alan J. Perlis, Epigram #32
Your insert routine has problems. I suggest you make your searchBookTree just return a null pointer when it doesn't find anything. Do not use that routine in the implementation of insertBook. Rather, you can write insertBook recursively also:
private:
// Inserts bookNode into tree, returning new tree:
Book *insertBookHelper(Book *tree, Book *bookNode) {
if (tree == NULL)
return bookNode; // bookNode becomes new tree
// no need to call strcmp twice!!!
int cmp = strcmp(title, bookNode->title);
if (cmp < 0) {
tree->left = insertBookHelper(tree->left, bookNode->title);
else if (cmp > 0)
tree->right = insertBookHelper(tree->right, bookNode->title);
else {
// Uh oh! Tree already contains that title, what to do?
// Answer: update!
// I don't know how to write this because I don't know
// how your Book class handles the memory for the strings,
// and what other members it has besides the title.
// this could be a possibility:
// bookNode->left = tree->left; // install same child pointers
// bookNode->right = tree->right; // into bookNode.
// *tree = *bookNode; // if Book has a sane copy constructor!!!
}
return tree;
}
public:
void insertBook(Book *node) {
tree = insertBookHelper(tree, node);
}
Do you see how the recursion works? It's a little different from the pure search. Each recursive level handles the insertion into the subtree and returns the new subtree. Often, the returned tree is exactly the same as the tree that went in! But when inserting into an empty tree, the returned tree is not the same: the tree that went in is a null pointer, but a non-null pointer comes out. This trick of pretending that we are making a new tree and returning it as a replacement for the old tree makes for smooth code.

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