Destructor, doesn't delete my object - c++

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.

Related

How to correctly delete an allocated array (queue data structure)

I created a queue data structure using a struct and a dynamically allocated array, I don't understand what is the right way to free or delete it without any memory leaks.
I have tried using the following:
delete[] q->data;
delete[] &(q->data);
delete &(q->data);
#include "queue.h"
void initQueue(queue* q, unsigned int size)
{
q->maxSize = size;
q->size = 0;
q->data = new unsigned int[size];
q->front = 0;
q->rear = 0;
}
void enqueue(queue* q, unsigned int newValue)
{
if (q->size != q->maxSize)
{
q->data[q->rear] = newValue;
q->size++;
q->rear++;
}
else
{
std::cout << "Queue is full! you can clean it and initialize a new one" << std::endl;
}
}
int dequeue(queue* q)
{
int i = 0;
if (q->size == 0)
{
std::cout << "Queue is empty!" << std::endl;
return EMPTY;
}
else
{
q->front++;
q->size--;
return q->data[q->front];
}
}
void cleanQueue(queue* q)
{
//the delete function
}
The technical right answer here is to delete q->data, as others have suggested. But...
right way to free or delete it without any memory leaks
The right way in C++, unless you're doing some exotic with allocation, is not to do your own memory management. Write a class that allocates in the constructor, and deletes in the destructor, as Chris suggested, is a great way to learn about RAII and how it saves you from the mental burden of manually writing "delete" everywhere.
But the right right way, if someone was paying me? I'd skip all that and use a vector.
#include <vector>
class MyQueue {
public:
MyQueue(unsigned int size) : data(size) { }
void enqueue(unsigned int value) { /* whatever... */ }
int dequeue() { /* whatever... */ }
private:
std::vector<unsigned int> data;
};
When this class goes out of scope or gets deleted, the vector will automatically be cleaned up. You don't even need to free or delete anything.

How to delete a node pointer

This is schoolwork. I haven't seen anything that really answers this directly, so I'm having a hard time fixing it. I have to create a linked node implementation of a max heap and I'm having difficulty with the deletion of a node after removing a value.
My Code:
template<class ItemType>
BinaryHeapNode<ItemType>* LinkedMaxHeap<ItemType>::getLastNode()
{
BinaryHeapNode<ItemType>* lastNode = rootPtr->getRightSiblingPtr();
BinaryHeapNode<ItemType>* prevLastNode = rootPtr;
while(lastNode != nullptr)
{
prevLastNode = lastNode;
lastNode = lastNode->getRightSiblingPtr();
}
return prevLastNode;
}
template<class ItemType>
bool LinkedMaxHeap<ItemType>::removeValue(ItemType value)
{
BinaryHeapNode<ItemType>* tempNode = rootPtr;
for (int i = 0; i < itemCount; i++)
{
if(tempNode->getItem() == value)
{
tempNode->setItem(getLastNode()->getItem());//set item
delete getLastNode(); //delete last node
getLastNode() = nullptr; //set last node null
getLastNode()->setRightSiblingPtr(nullptr); //last node should be different
itemCount--; //set it's sibling to null
heapRebuild(tempNode);
}
tempNode = tempNode->getRightSiblingPtr();
}
return true;
}
My issue is with getLastNode() = nullptr. VS is telling me that getLastNode() isn't an lvalue. That doesn't make sense to me because getLastNode is returning a pointer to a BinaryHeapNode, but it can't set that pointer to nullptr?
I thought this might be a problem with my logic of pointers (which is shaky at best) so I thought changing getLastNode() to return just a node would help. That did not. So I tried messing with the & operator and returning an address of the last node. Needless to say I haven't found the solution yet. If anyone can provide some sort of direction it would be appreciated. I'm just not entirely sure why it doesn't work.
EDIT:
Edited the code based on what arynaq mentioned. The errors went away, but now I have a bunch of linker errors I have to fix before I can test it. Will this code do what I want? I feel like it is just going to delete nodeToDelete and not get rid of the node in the heap.
template<class ItemType>
bool LinkedMaxHeap<ItemType>::removeValue(ItemType value)
{
BinaryHeapNode<ItemType>* tempNode = rootPtr;
BinaryHeapNode<ItemType>* nodeToDelete = getLastNode();
for (int i = 0; i < itemCount; i++)
{
if(tempNode->getItem() == value)
{
tempNode->setItem(nodeToDelete->getItem());
delete &nodeToDelete;
nodeToDelete = nullptr;
getLastNode()->setRightSiblingPtr(nullptr);
itemCount--;
heapRebuild(tempNode);
}
tempNode = tempNode->getRightSiblingPtr();
}
return true;
}
Ok, I'll try to help by explaining some things about pointers. Hopefully this will clarify some misconceptions and help you with your assignment.
When you get a copy of the pointer like so: mypointer* p = get_pointer(); and then you delete that, you are deleting the memory. But when you assign nullptr to this local variable, it wont affect the "source" of your pointer.
Here is a detailed example, showing where things can go wrong. If you never set v[0] to nullptr.
#include <iostream>
#include <vector>
struct Object {
~Object() {
std::cout << "Object destructor." << std::endl;
}
int val = 42;
};
struct OtherObj {
int val = 322;
};
void print_vec(const std::vector<Object*>& v) {
for (const auto& x : v) {
std::cout << x << std::endl;
}
}
int main(int, char**) {
// Init vector and print addresses.
std::vector<Object*> v(2);
print_vec(v);
// Init objects in vector and printit.
for (auto& x : v) {
x = new Object();
}
print_vec(v);
// Get a copy of a pointer and delete that. All good so far.
Object* pointer_to_delete = v[0];
delete pointer_to_delete;
// Assign nullptr to the temporary local pointer.
// Does nothing to the pointer in the vector.
pointer_to_delete = nullptr;
// Print the vector to prove it.
print_vec(v);
// On a non debug build, the memory will still have the last value.
// Careful! Cause of headaches here. This should be set to nullptr.
std::cout << v[0]->val << std::endl; // "No problem", certainly not nullptr.
// Now that we allocate a new object, v[0] will be overwritten.
OtherObj* bad_bad_boy = new OtherObj();
// Print the address of the new object, to show it was created at
// the old v[0] address.
std::cout << bad_bad_boy << std::endl;
// Bad things ensue...
std::cout << v[0]->val << std::endl;
return 0;
}
The output on clang is :
0x0
0x0
0x7ffa21c026c0
0x7ffa21c026d0
Object destructor.
0x7ffa21c026c0
0x7ffa21c026d0
42
0x7ffa21c026c0
322
As you can see, setting the local pointer to nullptr is not enough! I hope this clears up some things for you :)
Online version

C++ Stack Push/Print Implementation

I'm trying to make a stack implementation in C++ but when I try to print the stack,
it just prints the first element instead of the whole stack.
I've tested it and I'm pretty sure that my Push function is right, but I'm not sure.
#include <stdio.h>
#include <stdlib.h>
#include "stack.h"
int main(){
StackElement *stack = new StackElement();
stack->data = 20;
stack->Push(30,stack);
stack->Push(40,stack);
stack->Print(stack);
}
#include <stdio.h>
#include <stdlib.h>
class StackElement{
public:
int data;
StackElement* next;
StackElement();
void StackElement::Push(int value, StackElement *oldElement);
void StackElement::Print(StackElement *element);
};
StackElement::StackElement(){
next = NULL;
}
void StackElement::Push(int value, StackElement *oldElement){
StackElement *newElement = new StackElement();
newElement->data = value;
printf("Element added to stack: %d\n", newElement->data);
oldElement->next = newElement;
}
void StackElement::Print(StackElement *element){
while(element->next != NULL){
printf("%d\n",element->data);
element = element->next;
}
}
Your code kept loosing the previous pushed element, leaking memory, as #Beta described.
I suggest comparing my code below to your code. You'll see, I've moved the handling of the stack elements outside, just to be able to keep track of the first element. Also, notice that there is no pointer in the main function. That is what we expect from a class.
Stack_element is a struct really as there's not much point in making the Stack_element itself encapsulated, it is just an implementation detail of Stack.
So here's my code derived from yours
#include<iostream>
struct Stack_element{
int data;
Stack_element*next;
};
class Stack{
private:
Stack_element*last_data, first_data;
public:
Stack():last_data(NULL), first_data(NULL){}
void push(int data);
void print() const;
};
void Stack::push(int data)
{
Stack_element*p=new Stack_element();
p->data=data;
p->next=NULL;
if(last_data)
last_data->next=p;
else // empty stack
first_data=p;
last_data=p;
}
void Stack::print()
{
for(Stack_element*p=first_data;p;p=p->next)
std::cout << p->data << std::endl; // ** Do not use printf in c++. Ever. **
}
and in the main function just call
Stack stack;
stack.push(30);
stack.push(40);
stack.print();
REMARK: For a C++ish print you might want to do an ostream& print(ostream& os) instead, where
std::ostream& Stack::print(std::ostream& os)
{
for(Stack_element*p=first_data;p;p=p->next)
os << p->data << std::endl;
return os;
}
just to be able to write std::cout << stack.print() << std::endl;. The benefit of this is that you can easily redirect to a file.
std::ofstream ofs("yourfile.txt");
ofs << stack.print() << std::endl; // prints to file instead of screen.
Suppose this much works as planned:
StackElement *stack = new StackElement();
stack->data = 20;
stack->Push(30,stack);
Now your data looks like [20]->[30]
Now you attempt
stack->Push(40,stack);
So the Push method creates a new StackElement, gives it the value 40, and sets Stack to point to it: [20]->[40]. Notice that [30] has been lost.
Then the Print function:
while(element->next != NULL){
printf("%d\n",element->data);
element = element->next;
}
If there is only one element (whose next is NULL), this function will quit and print nothing. If there are two, this function will print the data of the first, then quit. And there will never be more than two, as long as Push has that bug.

Crash when deleting a pointer

I have an assignment to create a PriorityQueue structure and I'm having trouble with this piece of code. When I compile it on my compilator everything's fine, but I tried submitting it to ideone and I get the following error:
"glibc detected *** ./prog: double free or corruption".
I was able to track the part that was giving me this error and I found out that what causes the crash is me trying to delete a pointer at the destructor of my class. The problem is that I don't know why I cant delete it. I don't know a lot about pointers but I thought that if I used new to allocate memory I had to delete it after using it and I think this is what I'm trying to do. Here is my code:
struct PriorityQueue
{
LinkedList queue; LinkNode *it,*node;
int sz;
PriorityQueue(){
sz=0;
queue.head=NULL;
queue.tail=NULL;
it = NULL;
node=NULL;
}
~PriorityQueue(){
if(node != NULL) //this is causing the error.
delete [] node;
if(it != NULL)
delete [] it;
}
int size(){
return sz;
}
void enqueue(int x){
node = new LinkNode(x,NULL,NULL);
if(sz==0){
queue.insert_head(x);
sz++;
}
else{
if(x <= queue.head->value ){
queue.insert_head(x);
sz++;
}
else if( x>= queue.tail->value ){
queue.insert_tail(x);
sz++;
}
else{
it = queue.head;
for(int k=0;k<sz;k++){
if( (x>= it->value) && (x <= it->next->value) ){
node->next= it->next;
node->previous = it;
it->next->previous = node;
it->next = node;
sz++;
break;
}
it=it->next;
}
}
}
}
int dequeue_min(){
int min = queue.remove_head();
sz--;
return min;
}
int dequeue_max(){
int max= queue.remove_tail();
sz--;
return max;
}
};
int main()
{
PriorityQueue pq;
pq.enqueue(4);
pq.enqueue(2);
pq.enqueue(7);
pq.enqueue(-6);
pq.enqueue(0);
cout << pq.dequeue_min() << endl; // debe imprimir -6
cout << pq.dequeue_min() << endl; // debe imprimir 0
pq.enqueue(3);
cout << pq.dequeue_min() << endl; // debe imprimir 2
cout << pq.dequeue_min() << endl; // debe imprimir 3
return 0;
}
Thanks.
it and node point to objects, not arrays.
You cannot use the array form of delete[] on them.
Using delete[] will try to remove a pointer whose object is an array of some sort. There is another type of delete, that allows for the deletion of pointers to single objects. (Hint: it's pretty intuitive)
You are deleting it and node using delete []. They are not arrays. You can only use delete [] syntax on arrays or arrays of your objects. Remember the rule of thumb to use the similar delete and new commands for the same data types. If you have allocated memory by new, delete by delete. If you have allocated memory by new [], delete it by deete [].
It seems that it and node not only point to objects rather than arrays, as Slaks pointed out, it seems they also potentially point to the same thing. As a side note, you don't need to check for null before calling delete[] p or delete p: If the pointer p is null, this expression will have no effect.
It is unrelated to your question but please also note that your priority queue as O(n) (with n being the size) complexity. Typically, when implementing a priority queue you want to get O(log(n)) complexity. The easiest strategy to implement such a priority queue is a d-heap which, conveniently, lives in an array and is actually easier to maintain than your linked list (I think, at least).

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