Question about segmentation fault on std::stack push - c++

I am working on a octree traversal algorithm. The current implementation uses a std::queue for such purpose, working flawlessly. However, I would like to use for such traversal a std::stack, as a depth first search will give better performance, avoiding testing non needed nodes.
However, when changing from one structure to another, I start getting segmentation faults on the push() function. Here is the stack report from gdb:
0x00005555555ae28d in __gnu_cxx::new_allocator<voxelizer::Node*>::construct<voxelizer::Node*, voxelizer::Node* const&> (this=0x7fffffffd7f0, __p=0x5555559abde8, __args#0=<error reading variable>)
at /usr/include/c++/7/ext/new_allocator.h:136
136 { ::new((void *)__p) _Up(std::forward<_Args>(__args)...); }
(gdb) up
#1 0x00005555555acd1c in std::allocator_traits<std::allocator<voxelizer::Node*> >::construct<voxelizer::Node*, voxelizer::Node* const&> (__a=..., __p=0x5555559abde8, __args#0=<error reading variable>)
at /usr/include/c++/7/bits/alloc_traits.h:475
475 { __a.construct(__p, std::forward<_Args>(__args)...); }
(gdb) up
#2 0x00005555555ab63e in std::deque<voxelizer::Node*, std::allocator<voxelizer::Node*> >::push_back (this=0x7fffffffd7f0, __x=<error reading variable>) at /usr/include/c++/7/bits/stl_deque.h:1547
1547 _Alloc_traits::construct(this->_M_impl,
(gdb) up
#3 0x00005555555aa29f in std::stack<voxelizer::Node*, std::deque<voxelizer::Node*, std::allocator<voxelizer::Node*> > >::push (this=0x7fffffffd7f0, __x=<error reading variable>)
at /usr/include/c++/7/bits/stl_stack.h:226
226 { c.push_back(__x); }
I could not get my head around why, so I created a minimal, verifiable example where I could get rid of possible errors caused by any other part of the system. I reproduced the cotree Node structure, and created a small tree to traverse:
#include <iostream>
#include <stack>
#include <utility>
using namespace std;
// ==============================================================
class TestClass
{
public:
// Default constructor
TestClass()
: d(0)
, children(nullptr)
{
}
// Depth based constructor
TestClass(int d_)
: d(d_)
, children(nullptr)
{
if(d > 0)
{
children = new TestClass*[8];
for(int i = 0; i < 8; i++)
{
children[i] = new TestClass(d - 1);
}
}
}
// Copy constructor
TestClass(const TestClass & other_)
: d(0)
, children(nullptr)
{
_copy(other_);
}
// Move constructor
TestClass(TestClass && other_)
: d(0)
, children(nullptr)
{
_move(std::move(other_));
}
// Destructor
~TestClass()
{
_clearChildren();
}
// Copy assignment operator
TestClass & operator= (const TestClass & other_)
{
_copy(other_);
return *this;
}
// Move assignment operator
TestClass & operator= (TestClass && other_)
{
_move(std::move(other_));
return *this;
}
int depth()
{
return d;
}
TestClass ** getChildren()
{
return children;
}
private:
void _clearChildren()
{
if(children != nullptr)
{
for(int i = 0; i < 8; i++)
{
delete children[i];
}
delete[] children;
children = nullptr;
}
}
void _copy(const TestClass & other_)
{
d = other_.d;
_clearChildren();
if(other_.children != nullptr)
{
children = new TestClass*[8];
for(int i = 0; i < 8; i++)
{
children[i] = new TestClass(*(other_.children[i]));
}
}
}
void _move(TestClass && other_)
{
d = other_.d;
_clearChildren();
children = std::move(other_.children);
}
private:
int d;
TestClass ** children;
};
// ==============================================================
typedef TestClass * TestClassPtr;
// ==============================================================
int main(int argc, char ** argv)
{
TestClassPtr test = new TestClass(5);
stack<TestClassPtr> s;
s.push(test);
while(!s.empty())
{
TestClassPtr & next = s.top();
s.pop();
cout << "On depth " << next->depth() << endl;
if(next->getChildren() != nullptr)
{
std::cout << "Adding children" << std::endl;
for(int i = 0; i < 8; i++)
{
if(next->getChildren()[i]->getChildren() != nullptr)
{
s.push(next->getChildren()[i]);
}
}
}
}
cout << "Done" << endl;
return 0;
}
By running it I was able to reproduce the problem, in the push() method as well:
On depth 5
Adding children
On depth 3
Adding children
On depth 1
Adding children
Segmentation fault
So I went on to revising the documentation. Note that I'm using C++11. The requirements for a default std::stack can be inherited from the requirements of using a std::deque, as it is the default container used. Since C++11, the main requirement is to be a complete type and Erasable I made sure the destructor was accessible. Also, for the sake of safe proofing, I implemented a default constructor, copy constructor, move constructor, copy assignment, and move assignment.
So I believe my class is Erasable, but perhaps not complete. By modifying the traverse loop in the example and adding the "SAFE PROOF LINE" if:
if(next->getChildren() != nullptr)
{
std::cout << "Adding children" << std::endl;
for(int i = 0; i < 8; i++)
{
// SAFE PROOF LINE
if(next->getChildren()[i]->getChildren() != nullptr)
{
s.push(next->getChildren()[i]);
}
}
}
I was able to get rid of the segmentation fault. The nodes which this line discard are the leaf nodes, which does not have children and, thus, their children variable is a nullptr.
My questions:
Does this means a nullptr pointer makes a type incomplete?
The point of using this raw memory double pointer is to safe as much
memory as possible, is there anyway I can make it work without having
to substitute it for a stack array or a std::vector?
Thanks.

Seems to go wrong right from the start
while(!s.empty())
{
TestClassPtr & next = s.top();
s.pop();
next is a reference to the object on the top of the stack, but the very next line removes that object, so the reference becomes invalid.
Simple answer is to not use a reference and just copy the top of the stack.
while(!s.empty())
{
TestClassPtr next = s.top();
s.pop();

gdb says that the push argument is invalid:
push (this=0x7fffffffd7f0, __x=<error reading variable>)

Related

Memory leak in C++ (Valgrind)

I implement the stack with a minimum. In this program, I get an error from valgrind. Something is wrong with the push() and main() functions. When I add delete st; to the push() function, I get even more errors. I check it through valgrind ./a.out. Sorry for the long code. I also wrote the rest of the functions for stack. But there is no error in them, I left those in the code where there may be an error.
#include <cstring>
#include <iostream>
struct Stack {
int data;
int min;
Stack* next;
};
void Push(Stack** top, int n) {
Stack* st = new Stack();
st->data = n;
if (*top == NULL) {
*top = st;
(**top).min = n;
} else {
st->min = ((n <= (**top).min) ? n : (**top).min);
st->next = *top;
*top = st;
}
std::cout << "ok" << std::endl;
}
void Pop(Stack** top) {
if (*top != NULL) {
std::cout << (**top).data << std::endl;
*top = (*top)->next;
} else {
std::cout << "error" << std::endl;
}
}
int main() {
Stack* top = nullptr;
int m;
std::cin >> m;
std::string str;
for (int i = 0; i < m; ++i) {
std::cin >> str;
if (str == "push") {
int value;
std::cin >> value;
Push(&top, value);
}
if (str == "pop") {
Pop(&top);
}
}
delete top;
}
When you just delete top, you destruct it (in your case it's nothing, but you can distract yourself for reading about destructors if interested) and free the dynamic memory allocated for top. However, you actually want to also delete top->next, top->next->next (if present) etc. A hotfix:
while (top) { // same as "while (top != nullptr) {"
Stack* next = top->next; // we can't use `top` after we `delete` it, save `next` beforehand
delete top;
top = next;
}
Now, about more general things. The course teaches you some really old C++ (almost just plain C; even C here is bad though). At the very least, your whole Push() can be replaced (thanks to lvalue references (Type&), std::min and aggregate initialization) with:
void push(Stack*& top, int n) {
top = new Stack{n, std::min(n, top ? top->min : n), top};
std::cout << "ok\n";
}
I'm new to C++ programming. I used to write in Python
Good job. Sadly, such teaching shows C++ as something too old and horrifying.
Edit
here's a new in Push, so there should most likely be a delete in Pop
That's right (thanks to #molbdnilo). You should delete popped elements instead of just leaking them.

C++ Dynamic Member Arrays Deleted Right Before Destructor Called

I'm working on an AI project and have started to implement a NeuralNetwork class. I just want to get something basic down so I used some malloc statements with some placement news and finally delete[]s.
However, once the NeuralNetwork object (created on the stack in the main function) is about to be deleted (I set a breakpoint at the start of the destructor), my arrays seem to have been prematurely deleted (value 0xcccccccc) and the delete[] statements therefore throw access violations.
Through further investigation I found out that this deleting happens right between the last Vector object being destructed and the start of the destructor of my NeuralNetwork object being called.
I made sure to implement both copy constructors and assignment operators to make sure no copying was taking place without me noticing.
I'm really baffled with this problem and hope that someone can catch my mistake. Source code will follow:
NeuralNetwork::NeuralNetwork(const std::initializer_list<size_t>& l):
m_size(l.size() - 1),
m_weights(static_cast<Matrix<double>*>(malloc(sizeof(Matrix<double>) * m_size))),
m_biases(static_cast<Vector<double>*>(malloc(sizeof(Vector<double>) * m_size)))
{
size_t index = 0;
auto itr = l.begin();
for (auto next = itr + 1; next != l.end(); ++next, ++itr, ++index)
{
new (m_weights + index) Matrix<double>(*next, *itr);
new (m_biases + index) Vector<double>(*next);
}
}
NeuralNetwork::NeuralNetwork(const NeuralNetwork& nn) :
m_size(nn.m_size),
m_weights(static_cast<Matrix<double>*>(malloc(sizeof(Matrix<double>)* m_size))),
m_biases(static_cast<Vector<double>*>(malloc(sizeof(Vector<double>)* m_size)))
{
for (size_t index = 0; index < m_size; ++index)
{
new (m_weights + index) Matrix<double>(nn.m_weights[index]);
new (m_biases + index) Vector<double>(nn.m_biases[index]);
}
}
NeuralNetwork::NeuralNetwork(NeuralNetwork&& nn) noexcept :
m_size(nn.m_size),
m_weights(nn.m_weights),
m_biases(nn.m_biases)
{
nn.m_size = 0;
nn.m_weights = nullptr;
nn.m_biases = nullptr;
}
NeuralNetwork::~NeuralNetwork()
{
delete[] m_weights; // exception thrown here, value is 0xcccccccc, nullptr
delete[] m_biases;
}
Main code:
int main()
{
NeuralNetwork nn{ 2, 1 };
Vector<double> input(2);
input.Get(0) = 0.5;
input.Get(1) = 0.25;
Vector<double> output = nn.Forward(input); // just does the math, nothing special
for (size_t i = 0; i < output.GetSize(); ++i)
std::cout << output.Get(i) << " ";
std::cout << std::endl;
}
In case any important source code is missing please let me know, thanks!
There is a big difference between malloc/free and new/delete and new[] / delete[]
malloc will allocate an unformated chunk of memory and free will free it
new will allocate and initialize that region and delete will call the destructor
sometimes it might work to use malloc and delete but it's a bad idea
new[] will also keep a few extra info before the the returned memory to know how many objects need to be deleted
Usefull links:
https://www.geeksforgeeks.org/placement-new-operator-cpp/
You should never write such code:
You should never have to use malloc/free in a C++ program.
Allocation and desallocation should match.
Dynamic memory allocation has surely more overhead that default initialization you try to avoid.
Your code would miserably failed if initializer list is empty.
Code has memory leaks.
If you define a copy constructor, then you should also define assignment operator (same for move constructor).
Standard library already do most relavant optimization. For example,, for a std::vector the constructor of an item will be only called when you call emplace_back.
You should really write standard code. It does not worth the trouble to write bugged code for marginal performance improvement.
Your class declaration should really look something like:
class NeuralNetwork
{
public:
NeuralNetwork(const std::initializer_list<size_t>& l);
// Other constructors as appropriate hereā€¦
private:
std::vector<Matrix<double>> m_weights;
std::vector<Vector<double>> m_biases;
};
And constructor should look similar to that (code not tested):
NeuralNetwork::NeuralNetwork(const std::initializer_list<size_t>& l):
{
if (l.empty()
{
// might assert in debug or throw an exception...
return;
}
m_weights.reserve(m_size);
m_biases.reserve(m_size);
auto itr = l.begin();
for (auto next = itr + 1; next != l.end(); ++next, ++itr, ++index)
{
m_weights.emplace(*next, *itr);
m_biases.emplace(*next);
}
}
Other constructors, assignment operators and destructors should be easier to implement, more robust and performance very similar.
As you are using C++11 features already, you can also use std::vector::emplace_back(), which will deal with placement new internally.
Example:
#include <iostream>
#include <initializer_list>
#include <vector>
template<class T> class Matrix {
public:
Matrix() {std::cout << "Matrix() " << this << std::endl;}
Matrix(int width,int height):w(width),h(height) {std::cout << "Matrix(" << w << "x" << h << ") " << this << std::endl;}
~Matrix() {std::cout << "Matrix(" << w << "x" << h << ") " << this << " goodbye" << std::endl;}
private:
int w,h;
};
class NN {
public:
NN()=default;
NN(const std::initializer_list<size_t> &l);
private:
std::vector<Matrix<double>> m_weights;
};
NN::NN(const std::initializer_list<size_t> &l) {
m_weights.reserve(l.size()-1); // or deal with move constructors
auto itr = l.begin();
for (auto next = itr + 1; next != l.end(); ++next, ++itr)
{
m_weights.emplace_back(*next, *itr);
}
}
int main() {
NN test{2,3,3,2};
return 0;
}
Output (from https://ideone.com/yHGAMc):
Matrix(3x2) 0x5638f59aae70
Matrix(3x3) 0x5638f59aae78
Matrix(2x3) 0x5638f59aae80
Matrix(3x2) 0x5638f59aae70 goodbye
Matrix(3x3) 0x5638f59aae78 goodbye
Matrix(2x3) 0x5638f59aae80 goodbye
So the default constructor was not involved and objects were destructed properly.

c++ code works properly but the process ends with termination instead return 0

i wrote a simple c++ code in codeblocks and i implemented stack and graph classes for that with dynamic memory allocation.
my code works properly and gives correct output but at the end it shows ***.exe has stopped working error and shows "Process terminated with status -1073741819" in build log.
i tried GNU gdb 6.8 debugger and it couldn't find any errors.
this problem was made after imlementing stack class, so this is my code if it can helps solving problem:
class stack
{
vertex* d;
int end;
public:
stack()
{
end=0;
d=NULL;
}
void create(int n)
{
d=new vertex[n];
}
vertex top()
{
return d[end];
}
void push(vertex y)
{
end++;
d[end]=y;
}
vertex pop()
{
end--;
return d[end+1];
}
~stack()
{
if (d!=NULL)
delete d;
}
};
vertex class is also declared before stack.
for some inputs, debugger says "Program received signal SIGSEGV, Segmentation fault."
edit: main asked:
int main()
{
G graf;
graf.get();
stack tree;
tree.create(graf.q()-1);
int q=0;
int i=0;
int u=0;
while (u<graf.q()-1)
{
tree.push(graf.u[i]);
if (graf.u[i].r[0]->flag > 0 && graf.u[i].r[1]->flag > 0 && u>=q)
tree.pop();
else
{
u++;
if (graf.u[i].r[0]->flag==0)
q++;
if (graf.u[i].r[1]->flag==0)
q++;
graf.u[i].r[0]->flag++;
graf.u[i].r[1]->flag++;
cout << tree.top().r[0]->name << " - " << tree.top().r[1]->name << '\n';
}
i++;
}
return 0;
}
i even tried adding a cout just before return 0 and my text printed.
Your code is wrong:
void push(vertex y)
{
end++;
d[end]=y;
}
Should be:
void push(vertex y)
{
d[end]=y;
end++;
}
Else, first pushed item goes to position 1instead of position 0.
Moreover, stack::top() returns next item, not last pushed:
vertex top()
{
return d[end];
}
should be:
vertex top()
{
return d[end-1];
}
I'm pretty sure you seg fault is due to unallocated memory being accessed, add assertions to have the program notify you when something gets wrong, like that:
class stack
{
vertex* d;
int cur;
int capacity;
public:
stack()
{
cur=0;
capacity=0;
d=NULL;
}
void create(int n)
{
assert( d == NULL );
capacity = n;
d=new vertex[n];
}
vertex top()
{
assert( cur != 0 );
return d[cur-1];
}
void push(vertex y)
{
cur++;
assert( cur < capacity );
d[cur]=y;
}
vertex pop()
{
assert( cur > 0 );
cur--;
return d[cur+1];
}
~stack()
{
if ( d != NULL )
delete [] d;
}
};
Then, run again, you'll see where you get an assertion.
Finally, check vertex copy constructor works fine, because pushing/poping does a lot of vertexcopy, if there's something wrong here, it could cause seg fault.

Class creating multiple objects of another class at the same memory location (C++)

So, I've got this class that contains a vector of another class. Whenever I try to push a new object into this vector, it's creating that object at the same memory location each time.
The (hopefully) relevant code:
class FSM{
private:
std::vector<Node> nodeList;
int cap;
int obs;
int topNode;
public:
FSM(int nodeCap, int numObs){
cap = nodeCap;
obs = numObs;
topNode = -1;
}
bool addNode(){
if (isFull()) return false;
nodeList.push_back(Node(obs));
topNode++;
return true;
}
Now, if I create a stand-alone Node object in my main function and cout the &node, I get different memory locations. But the ones created in the FSM class are always the same. Also, if I change anything in one of the Nodes stored by the FSM class, it changes it for all of them. I have no idea what's going on.
EDIT: As requested, here is the Node class. Just gonna post the whole thing, not sure what is relevant.
class Node{
private:
std::vector<int> connects;
int action;
public:
Node(int numObs){
for(int i = 0; i < numObs; i++){
connects.push_back(-1);
}
srand(time(NULL));
}
void setConnections(std::vector<int> newVec){
for (int i = 0; i < connects.size(); i++){
connects[i] = newVec[i];
}
}
int getConnection(int index){
return connects[index];
}
std::vector<int> getConnectionList(){
return connects;
}
void setAction(int act){
action = act;
}
int getAction(){
return action;
}
void setRandomConnections(int numNodes){
for (int i = 0; i < connects.size(); i++){
connects[i] = rand() % numNodes;
}
}
};
EDIT the Second: Here's what my main is doing.
int main(){
FSM myFSM(5, 3);
while (!myFSM.isFull()){
myFSM.addNode();
std::cout << &myFSM.getTopNode(); // getTopNode() returns the most recent
// node.
}
}
If getTopNode does what I think it does, you're printing the address of a temporary object (aka a copy of the top node, not the top node itself). So that code is meaningless.
Here I've implemented a print function for the locations of the nodes in FSM:
void printNodeLocations()
{
for(Node& n : nodeList) { std::cout << &n << std::endl; }
}
And I get different ones as expected:
0x8ad3018
0x8ad301c
EDIT: I cannot reproduce your claim that changing one node changes all of them. See updated code
This line:
std::cout << &myFSM.getTopNode();
probably prints the address of a temporary object, not the actual object in the vector. This will be true if you're not returning by reference but rather by value.
So it's not weird if the temporary happens to be created at the same location each time, since after the temporary dies, its location in memory is free to be used again later.
In order to get the actual object rather than a copy of it, getTopNode() needs to do:
Node& FSM::getTopNode()
{
if (nodeList.empty()) {
// Up to you how to handle this error.
}
return nodeList.back();
}
Of course, if your current getTopNode() implementation actually already returns a pointer:
Node* FSM::getTopNode()
then your problem is that you're printing out the address of the pointer rather than the pointer itself. In that case you should print with:
std::cout << myFSM.getTopNode();
Nothing happens similar to yours.
#include <iostream>
#include <vector>
class Node{
private:
std::vector<int> connects;
int action;
public:
Node(int num){
for(int i = 0; i < num; i++){
connects.push_back(i);
}
}
std::vector<int> getConn()
{
return connects;
}
};
class FSM{
private:
std::vector<Node> nodeList;
public:
FSM(){}
void addNode(int size){
Node l(size);
std::cout<<"temp_address "<<&l<<"\n";
nodeList.push_back(l);//use of default copy constructor
}
void printList(){
std::vector<int> p;
for (int i=0; i<nodeList.size(); i++)
{
std::cout<<"Node_arr_num "<<i<<" mem_address "<<&nodeList[i]<<"\nConnections:";
p=nodeList[i].getConn();
for (int j=0; j<p.size(); j++)
std::cout<<" "<<p[j];
std::cout<<"\n";
}
}
};
int main()
{
FSM f;
f.addNode(5);
f.addNode(10);
f.addNode(3);
f.printList();
return 0;
}
Result:
temp_address 0xbfea7660
temp_address 0xbfea7660
temp_address 0xbfea7660
Node_arr_num 0 mem_address 0x8dab098
Connections: 0 1 2 3 4
Node_arr_num 1 mem_address 0x8dab0a8
Connections: 0 1 2 3 4 5 6 7 8 9
Node_arr_num 2 mem_address 0x8dab0b8
Connections: 0 1 2
Be careful with adding nodes later, when your app will grow. Temporary l object (ore your Node(obs)) must be copied with explicit copy constructor of class Node if Node will be more complex (contains fields with dynamic allocated memory).

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