stack overflow during recursion in C++ - c++

I wrote a template class for Singly linked list. For printing values in reverse order, I implemented traverse_reverse() function using recursion. When the number of elements in list reaches near 4000, calling this function threw stack overflow error.
At such range of numbers, I am not sure if stack overflow should happen.
Environment is Visual Studio 2019 Community edition, Windows 10 64 bit OS.
My questions are:
How can I avoid stack overflow
How can I increase the size of stack at runtime.
Below is the code snippet:
#pragma once
#include <mutex>
#include <iostream>
namespace MyDS
{
template <typename T>
struct Node
{
T* m_pData = nullptr;
Node* m_pNext = nullptr;
};
template <class T>
class sList
{
Node<T>* m_pHead = nullptr;
Node<T>* m_pCurrentNode = nullptr;
int m_Size = 0;
std::mutex m_ListMutex;
public:
bool insert_front(T val);
bool insert_last(T val);
bool insert_anywhere(T val, int loc);
bool remove(T val);
//bool remove(int loc);
bool remove_front();
bool remove_last();
void traverse();
void traverse_reverse();
bool emptyList();
int getSize();
private:
void traverse_reverse(Node<T>* pNode);
};
template<typename T>
void sList<T>::traverse_reverse(Node<T>* pNode)
{
if (pNode->m_pNext != nullptr)
traverse_reverse(pNode->m_pNext);
std::cout << *pNode->m_pData << " ";
}
template<typename T>
bool sList<T>::emptyList()
{
bool ret = false;
if (getSize() > 0)
{
std::lock_guard<std::mutex> lg(m_ListMutex);
Node<T>* pTempNode = m_pHead, pTempNode1 = nullptr;
while (pTempNode->m_pNext!= nullptr)
{
pTempNode1 = pTempNode->m_pNext;
delete pTempNode->m_pData;
delete pTempNode;
pTempNode = pTempNode1;
}
delete pTempNode->m_pData;
delete pTempNode;
pTempNode->m_pData = pTempNode1->m_pData = m_pHead->m_pData = m_pCurrentNode->m_pData = nullptr;
pTempNode = pTempNode1 = m_pHead = m_pCurrentNode = nullptr;
m_Size = 0;
}
ret = true;
return ret;
}
template<typename T>
int sList<T>::getSize()
{
return m_Size;
}
template<typename T>
bool sList<T>::insert_front(T val)
{
Node<T>* pNode = new Node<T>;
pNode->m_pData = new T(val);
if (getSize() > 0)
{
pNode->m_pNext = m_pHead;
}
m_pHead = pNode;
m_Size++;
return true;
}
template<typename T>
bool sList<T>::insert_last(T val)
{
Node<T>* plastNode = m_pHead;
while (plastNode->m_pNext!= nullptr)
plastNode = plastNode->m_pNext;
plastNode->m_pNext = new Node<T>;
plastNode->m_pNext->m_pData = new T(val);
return true;
}
template<typename T>
bool sList<T>::insert_anywhere(T val, int loc)
{
return true;
}
//template<typename T>
//bool sList<T>::remove(int loc)
//{
// return true;
//}
template<typename T>
bool sList<T>::remove_front()
{
std::lock_guard<std::mutex> lg(m_ListMutex);
Node<T>* pNode = m_pHead;
m_pHead = m_pHead->m_pNext;
delete pNode->m_pData;
delete pNode;
m_Size--;
return true;
}
template<typename T>
bool sList<T>::remove_last()
{
Node<T>* plastNode = m_pHead;
std::lock_guard<std::mutex> lg(m_ListMutex);
if (getSize() > 1)
{
while (plastNode->m_pNext->m_pNext != nullptr)
plastNode = plastNode->m_pNext;
Node<T>* pNode = plastNode->m_pNext;
plastNode->m_pNext = nullptr;
delete pNode->m_pData;
delete pNode;
pNode->m_pData = pNode = nullptr;
m_Size--;
}
else if(getSize() == 1) // Only 1 node
{
delete m_pHead->m_pData;
delete m_pHead;
m_pHead->m_pData = m_pHead = nullptr;
m_Size--;
}
else // No node available
{
//Nothing to do
}
return true;
}
template<typename T>
bool sList<T>::remove(T val)
{
bool ret = false;
Node<T>* pNode = m_pHead;
Node<T>* pNodeNext = pNode->m_pNext;
if (pNode->m_pData == val)
{
ret = remove_front();
}
else if (pNodeNext->m_pData == val)
{
pNode->m_pNext = pNodeNext->m_pNext;
pNodeNext->m_pNext = nullptr;
delete pNodeNext->m_pData;
delete pNodeNext;
pNodeNext->m_pData = pNodeNext = nullptr;
ret = true;
m_Size--;
}
else
{
while (pNodeNext->m_pData != val)
{
pNode = pNodeNext;
pNodeNext = pNodeNext->m_pNext;
}
if (pNodeNext == nullptr)
ret = false;
else
{
pNode->m_pNext = pNodeNext->m_pNext;
pNodeNext->m_pNext = nullptr;
delete pNodeNext->m_pData;
delete pNodeNext;
pNodeNext->m_pData = pNodeNext = nullptr;
m_Size--;
ret = true;
}
}
return ret;
}
template<typename T>
void sList<T>::traverse()
{
m_pCurrentNode = m_pHead;
while (m_pCurrentNode->m_pNext != nullptr)
{
std::cout << *m_pCurrentNode->m_pData<<" ";
m_pCurrentNode = m_pCurrentNode->m_pNext;
}
std::cout << *m_pCurrentNode->m_pData;
std::cout << std::endl;
}
template<typename T>
void sList<T>::traverse_reverse()
{
m_pCurrentNode = m_pHead;
traverse_reverse(m_pCurrentNode);
std::cout << std::endl;
}
}
#include "MyDS.h"
int main()
{
MyDS::sList<int> myList;
for(int i = 0; i <= 3987; ++i)
myList.insert_front(i);
myList.traverse_reverse(); //Recursion
// myList.traverse();
return 0;
}

As the other answers have pointed out, you have not provided the full code. That said, guessing from the code you have given, I believe that the issue is that you are right about the stack overflow occurring due to too many function calls on the stack when the list of elements is sufficiently long.
In general, it is best to avoid having lots and lots of function calls on the stack. Increasing the stack size is most often not a good solution. See for instance why is stack memory size so limited? for some discussions on this topic.
A single-linked list might be difficult reg. this. One option might be to reverse the single-linked list (maybe creating a new single-linked list), and then just traversing that one (possibly deleting the created list afterwards). A double-linked list would be able to do it very easily and efficiently, since you can just find the last element and then go backwards from there.

If you want to avoid stack overflow, don't use recursion. a simple while can do same job without requiring further resources from stack:
template<typename T>
void sList<T>::traverse_reverse(Node<T>* pNode)
{
while (pNode != nullptr){
std::cout << *pNode->m_pData << " ";
pNode=pNode->m_pNext;
}
}
To increase stack size: Increase stack size in c++
However, in case the above code does not work, I suspect your problem is elsewhere. my initial guess is that you have an infinite loop (in recursion). Let's say for some reason, you have circular dependencies in your list: every node has his m_pNext filled with something other then nullptr. Recursion will never end, hence stackoverflow error. The code above will not work.
Usually circular dependencies arise from incorrect implementation of insert or remove methods. If for some reason you update your pointer in removal incorrectly to another node, it can cause circular dependency.
You can use following code to check for circular dependencies:
template<typename T>
void sList<T>::traverse_reverse(Node<T>* pNode)
{
Node<T>* org_p=pNode;
while (pNode->m_pNext != nullptr){
pNode=pNode->m_pNext;
if(org_p==pNode){
std::cout << "Circular Dependency";
break;
}
}
std::cout << "No Circular Dependency";
}

For printing values in reverse order, I implemented traverse_reverse() function using recursion.
Recursion (unless optimized by your compiler as tail-recursive calls) always consume call stack space. See also this draft report for examples of interesting GCC optimizations. Probably, your C++ compiler is capable of doing similar optimizations.
You could instead prefer consuming heap space, e.g. use intermediate standard C++ containers to keep temporary data.
You could be interested by continuation-passing style. Sometimes it enables you to avoid recursion (and use more heap memory instead).
You can find C++ implementations (recent GCC or Clang comes to mind...) whose source code of std::vector or std::list is open source and readable. You might be surprised by their complexity, related to the rule of five.
If you compiled your C++ code with a recent GCC, you could have used g++ -Wall -Wextra -g -fstack-protector -Wstack-usage=2048 perhaps combined with -O2 to be warned of large call frames.
You might be interested in static source program analysis tools such as Frama-C++, Coverity, Clang static analyzer, or the address sanitizer, or in writing your own GCC plugin to build a call graph and sometimes detect potential stack overflows (but be aware of Rice's theorem). See also valgrind.

Related

How to return a reference of the top object in a stack c++

I am attempting to create a peek()/top() function for my Stack class using templates, and have this issue where I am unsure of how to return the topmost object of the stack. So far, I have been able to check if the top object is empty, if it's not then move a current pointer to point to the head of the Stack and return that pointer's data. However, this returns an int and I am wanting to return a reference to the object, not the data in the object. Any ideas of how to do this?
Stack.h
#include "LinkedList.h"
#include <cstdlib>
#include <iostream>
template <typename T>
class LStack
{
public:
//mutator member functions
LStack(){numofstacks = 0;}
~LStack(){}
void push(const T& obj) //insert obj at the top of the stack
{
data.add_to_head(obj);
numofstacks = numofstacks + 1;
}
T pop() //remove and return the top object from the stack, error if stack is empty
{
if (is_empty()){
std::cout << "Stack is empty" << std::endl;
exit(0);
}
else
{
data.remove_from_head();
numofstacks = numofstacks - 1;
if (is_empty()){
exit(0);
}
else{
data.move_to_head();
return data.get_current();
}
}
}
//return a reference to the ***OBJECT** at the top of the stack, NULL if the stack is empty, (also refered to as top())
T& peek()
{
if (is_empty()){
std::cout << "Stack is empty" << std::endl;
exit(0);
}
else{
data.move_to_head();
return data.get_current(); // Need to return reference to object here
}
}
bool is_empty() const //return a boolean indicating whether the stack is empty
{
return (size() == 0);
}
int size() const //return the number of objects in the stack
{
return numofstacks;
}
private:
LinkedList<T> data;
int used;
int numofstacks;
};
LinkedList
#include "Node.h"
#include <cstdlib>
#include <iostream>
template <typename T>
class LinkedList
{
public:
LinkedList() //constructor
{
head = NULL;
tail = NULL;
current = NULL;
list_length=0;
}
~LinkedList()//destructor since we're creating the linked list on the heap
{
while (head != NULL)
{
remove_from_head();
}
tail = NULL;//not sure if in or out of while loop
}
LinkedList(T& item)
{
head = new Node<T>(item);
tail = head;
current = tail;
list_length = 1;
}
void add_to_head(const T& item)
{
if (list_length == 0){ //if list is empty
head = new Node<T>(item);
tail = head;
current = tail;
list_length = 1;
}
else //Else if list is not empty.. Insert node at the head
{
Node<T>* head_insert = new Node<T>(item);
head->set_prev(head_insert);
head_insert->set_next(head);
head = head_insert;
list_length++;
head_insert = NULL;
}
}
void add_to_tail(const T& item)
{
if (list_length == 0){
head = new Node<T>(item);
tail = head;
current = tail;
list_length = 1;
}
else //insert node at tail
{
Node<T>* tail_insert = new Node<T>(item);
tail->set_next(tail_insert);
tail_insert->set_prev(tail);
tail = tail_insert;
list_length++;
tail_insert = NULL;
}
}
void remove_from_head()
{
if (list_length == 0){
return;
}
else if (list_length == 1){
delete head;
head = NULL;
tail = NULL;
current = NULL;
list_length--;
return;
}
else
{
Node<T>* temp_head = head;
head = temp_head->get_next();
delete temp_head;
list_length--;
temp_head = NULL;
}
}
void remove_from_tail()
{
if (list_length == 0){
return;
}
else if (list_length == 1)
{
delete head;
head = NULL;
tail = NULL;
current = NULL;
list_length--;
return;
}
else
{
Node<T>* temp_tail = tail;
tail = temp_tail->get_prev();
delete temp_tail;
list_length--;
temp_tail = NULL;
}
}
std::size_t size()
{
return list_length;
}
T get_current()
{
return current->get_data();
}
void forward()
{
current = current->get_next();
}
void back()
{
current = current->get_prev();
}
void move_to_head()
{
current = head;
}
void move_to_tail()
{
current = tail;
}
private: //private members
Node<T>* head; //point to start of list
Node<T>* tail; //point to end of list
Node<T>* current; //optional - used to refer to a node in our list
std::size_t list_length;
};
Error Message
In file included from main.cpp:1:0: LStack.h: In instantiation of ‘T&
LStack::peek() [with T = int]’: main.cpp:29:19: required from
here LStack.h:52:28: error: cannot bind non-const lvalue reference of
type ‘int&’ to an rvalue of type ‘int’
return data.get_current(); // Need to return reference to object here
This method in your linked list:
T get_current()
returns an rvalue of type T. rvalues are constant by definition. Change to prototype of get_current to:
T& get_current()
You may also need to change the return type for Node::get_data to return a reference as well.
The short answer is that as much as it may seem to you like you want to do this, you really don't, because there's no way to do it cleanly. Even having pop() return the top object by value is highly problematic. In particular, if the copy constructor for the stored type can throw, it's pretty much a disaster waiting to happen--if the copy constructor throws as its trying to copy the return value, the situation is basically unrecoverable. The value has already been removed from the stack, but it isn't being returned to the caller, so it's completely lost.
The standard library gets around this by not having pop return anything. So, you pop an item from the stack in two steps: first you use top() to get the top item on the stack, then you use pop() to just remove the item on top of the stack:
auto top_item = your_stack.top();
your_stack.pop();
If the copy constructor throws as you're trying to retrieve the top-of-stack item, then the your_stack.pop() won't execute, so the stack hasn't been affected--the top item remains there. The pop() only executes if you've already successfully retrieved the top item into top_item.
Unfortunately, this is a little clumsy to use.
Even more unfortunately, since we're using two steps to pop the top item, it's essentially impossible to make this thread-safe--for that, you just about have to ensure that retrieving the item and removing it from the stack are a single atomic operation--either both of them happen without anything else interfering, or else neither one happens at all.
There is a design that meets that requirement, and (at least arguably) isn't quite as clumsy to use:
void pop(T &t) {
t = storage.back();
storage.pop_back();
}
Much like the previous example using the standard stack, if the t=storage.back(); line throws an exception, then the storage.pop_back(); simply won't execute--you haven't retrieved an item, but the items on the stack remain undisturbed.
For a simple concurrent stack, you could add a mutex and change the return type to bool, indicating whether the operation succeeded or not (again, you can't follow the typical single-threaded pattern of "if (!stack.empty()) stack.pop()`, since you're back to using two separate operations, so even if you find a non-empty stack, by the time you try to pop it, some other thread may have already done so, and your attempt at popping will fail.

c++ Linked List Memory Bug

For an assignment i have to build this. I just can't seem to see what i am doing wrong. When I am trying to run this code I keep seeing the pointer that my linked list stores it's starting location get pointed to garbage right in the middle. I don't know if Visual Studio is just hazing me or if I am miss assigning a pointer somewhere.
This is the main class i use to run my code
#include "stdafx.h"
#include "Iterator.h"
#include "Node.h"
#include "List.h"
#include <iostream>
int main()
{
int input = 0;
List<double> salaryList;
std::cin >> input;
Node<double> tim(7.0, nullptr);
Node<double> sim(input, nullptr);
Node<double> jim(7.5, nullptr);
salaryList.Add_back(&jim);
salaryList.Add_back(&tim);
salaryList.Insert_front(&sim);
Iterator<double> checkSalaries=salaryList.begin();
//std::cout << "printing all elements in Iterator" << std::endl;
while (checkSalaries.Is_item()){
double x = (*checkSalaries).value;
std::cout << x << std::endl;
checkSalaries++;
}
system("PAUSE");
return 0;
}
This is the code for the LinkedList, i just named it List :
#include "Iterator.h"
#include "Node.h"
template <class t>
class List
{
private:
Node<t>* start=nullptr;
Node<t>* end=nullptr;
int size = 0;
public:
List() {
start = nullptr;
end = nullptr;
}
~List() {
}
void Insert_front(Node<t> * input) {
if (start != nullptr)
{
input->setPoint(start);
start = input;
size++;
}
else {
start = input;
}
if (start->point != nullptr && end == nullptr) {
end = start->point;
size++;
}
}
void Add_back(Node<t> * input) {
if (end != nullptr) {
Node<t> temp = (*end);
temp.setPoint(input);
end = input;
}
else {
if (start != nullptr) {
start->point=input;
end = input;
}
else {
start = input;
}
size++;
}
}
Iterator<t> begin() const
{
Node<t> tempNode = *start;
Iterator<t> temp(&tempNode);
return temp;
}
void Remove_all()
{
List<Node<t>> temp;
start = temp.start;
end = temp.end;
size = 0;
}
int Size() const {
return size;
}
};
This is the Node code:
template <class T>
class Node {
public:
T value;
Node<T> * point;
Node(T first, Node<T> * second)
{
value = first;
point = second;
}
Node()
{
value = NULL;
point = nullptr;
}
void setPoint(Node<T> * input) {
point = input;
}
};
I am going to include here two images the first is what it looks like just before it goes bad, and the next is what happens right after, it seems to occur fairly at random but i have found that using cout always triggers it so i commented out that line, though that didn't resolve the issue.
Good StateBad State
On my first review, it seems the local variable in begin method is creating the issue. Please check my code below. I have commented out the temporary variable created in the begin method and instead made use of the pointer start. This should solve the issue.
Iterator<t> begin() const
{
// Node<t> tempNode = *start; <-- A local variable is used here
// Iterator<t> temp(&tempNode); <-- address of local variable passed to iterator.
Iterator<t> temp(start);
return temp;
}

recursive destructor for linked list

I tried to search the topic but all the threads I found used while loops.
However I would like to do this recursively:
template <typename S>
struct node {
S data;
node<S> * next;
};
this is the function I invoke in the destructor (pass the head as parameter) of the linked list:
void destroy(node<T> * n) {
if(n->next != NULL){
destroy(n->next);
}
delete n;
}
Unfortunately the result is a segmentation fault.
Can someone help me?
Edit: complete code
#include <iostream>
using namespace std;
template <typename T>
class List {
private:
template <typename S>
struct node {
S data;
node<S> * next;
};
node<T> * first;
node<T> * get_last_p() {
if(first != NULL){
return get_last(first);
}
return NULL;
}
node<T> * get_last(node<T> * n) {
if(n->next != NULL) {
return get_last(n->next);
} else {
return n;
}
return NULL;
}
void destroy(node<T> * n) {
if(n->next != NULL){
destroy(n->next);
}
delete n;
}
public:
List() {first->next = 0;}
~List() {destroy(first);}
void add(T element) {
node<T> * new_element = new node<T>;
new_element->data = element;
if(first == NULL){
first = new_element;
} else {
get_last_p()->next = new_element;
}
}
T get_last() {
return get_last_p()->data;
}
T get_first() {
return first->data;
}
};
From what I can see, in List's constructor, first is not initialized, and is then immediately accessed. That is undefined behavior.
Even if first was somehow initialized to null in an unreliable way, and that first->next = 0; wouldn't crash somehow, you'd also fail in your destructor's destroy, since destroy assumes its original argument is not null.
I assume you meant to
List() : first{ new node{} } { first->next = nullptr; }
If first is not meant to hold a value, then you're going to have to refactor your code to first initialize first to null - there's no working around that - and handle the case where first is null explicitely in all your code. You cannot assign first->next of a null, invalid or undefined pointer.

Linked list mistake at insertion

I'm trying to implement a simple doubly linked list with exposed nodes in C++ like this
(some methods omitted, should be pretty clear as to what they do):
template<typename T>
class Node
{
public:
Node(T _value)
{
m_Data = _value;
m_HasParent = false;
m_HasNext = false;
m_Parent = NULL;
m_Next = NULL;
}
void insertAfter(Node<T>* _item)
{
m_Next = _item;
m_HasNext = true;
_item->insertBefore(this);
}
void insertBefore(Node<T>* _item)
{
if(m_HasParent)
{
m_Parent->insertAfter(_item);
_item->insertAfter(this);
}
else
{
m_Parent = _item;
m_HasParent = true;
}
}
private:
T m_Data;
Node<T>* m_Parent;
Node<T>* m_Next;
bool m_HasParent;
bool m_HasNext;
};
template<typename T>
class LinkedList
{
public:
LinkedList()
{
m_Root = NULL;
m_HasRoot = false;
}
~LinkedList()
{
if(m_HasRoot)
{
delete m_Root;
}
}
void pushFront(T _value)
{
if(m_HasRoot)
{
Node<T>* node = new Node<T>(_value);
m_Root->insertBefore(node);
node->insertAfter(m_Root);
m_Root = node;
}
else
{
m_Root = new Node<T>(_value);
m_HasRoot = true;
}
}
void pushBack(T _value)
{
if(m_HasRoot)
{
Node<T>* last = m_Root;
while(true)
{
if(last->getHasNext())
{
last = last->getNext();
}
else
{
break;
}
}
Node<T>* node = new Node<T>(_value);
last->insertAfter(node);
return;
}
else
{
m_Root = new Node<T>(_value);
m_HasRoot = true;
}
}
T operator[](int _i)
{
Node<T>* last = m_Root;
for(int i = 0; i <= _i; i++)
{
if(last->getHasNext())
{
last = last->getNext();
}
else
{
break;
}
}
return last->getData();
}
private:
Node<T>* m_Root;
bool m_HasRoot;
};
However, when executing the following little test:
int main(int argc, char** argv)
{
LinkedList<int> l;
l.pushBack(0);
l.pushBack(1);
l.pushBack(2);
l.pushBack(3);
int count = l.getCount();
std::cout << "count: " << count << std::endl;
for(int i = 0; i < count; i++)
{
std::cout << "Value at [" << i << "]: " << l[i] << std::endl;
}
std::string s;
std::cin >> s;
return 0;
}
I expect to see the numbers 0, 1, 2, 3 printed out in this order, but this is what I get:
For some reason, the insertion to the back doesn't quite work. I find nothing fundamentally wrong with my code, can anybody spot the problem? This is on MinGW 5.1, 64bit, Windows 10 64bit. However, when executing this problem on runnable.com, everything seems to work just fine?? See this draft. Is this a bug in MinGW or some mistake on my part?
Edit 1
Now this is weird, sometimes it seems to work in runnable, and sometimes it yields the same result as on my local machine... I'm thoroughly confused.
Try changing the loop condition inside ListWidget::operator[] from i <= _i to i < _i. There should be no iteration for _i equal 0.
The sign is that you're starting printing from the second element i.e. 1 and printing the last element twice. You forgot about the root node.
Note that you're checking if a node has a succesor, but you aren't checking if the list is not empty (m_root != nullptr). Make this consistent - either remove all possible cases of UB or don't check anything.

Error reading memory while inserting at the head of a linked list

So I have this linked list class that does a great job on it's own functionally, however is pretty disgusting when it comes to actual memory usage (leaks, leaks everywhere).
So I'm going through implementing a basic smart pointer class into it so as to better handle memory, however I've hit a few rough points on the actual implementation part of this idea.
I've only specifically included what I think is relevant to the issue, however, if there is any parts not included that may prove useful, ask and I can post the whole thing.
main.cpp:
int main()
{
smartLinkedList<char*> moo2;
moo2.insertAtFront("tail");
moo2.insertAtFront("one");
moo2.insertAtFront("head");
for(int j = 0; j < moo2.length() ; j++)
cout << moo2.goToFromFront(j) << endl;
cin.ignore(1);
return 0;
}
smartLinkedList.h:
template <class type>
class smartLinkedList
{
private:
int size;
sPtr<node<type>> head;
public:
smartLinkedList(): head(NULL), size(0) {}
bool insertAtFront(type obj)
{
sPtr<node<type>> temp(new node<type>);
temp->data = obj;
temp->next = head.get();
//For future reference, &*head = head.get()
head = temp;
//delete temp;
size++;
return true;
}
type goToFromFront(int index)
{
sPtr<node<type>> temp = head;
for(int i = 0; i < index; i++)
{
temp = temp->next;
if(temp->next == NULL)
return temp->data;
}
return temp->data;
}
};
smartPointer.h:
#pragma once
class referenceCount
{
private:
int count;
public:
void add()
{
count++;
}
int release()
{
return --count;
}
};
//for non-learning purposes, boost has a good smart pointer
template <class type>
class sPtr
{
private:
type *p;
referenceCount *r;
public:
sPtr()
{
p = NULL;
r = new referenceCount();
r->add();
}
sPtr(type *pValue)
{
p = pValue;
r = new referenceCount();
r->add();
}
sPtr(const sPtr<type> & sp)
{
p = sp.p;
r = sp.r;
r->add();
}
~sPtr()
{
if(r->release() == 0)
{
delete p;
delete r;
}
}
type* get()
{
return p;
}
type& operator*()
{
return *p;
}
type* operator->()
{
return p;
}
sPtr<type>& operator=(const sPtr<type>& sp)
{
if (this != &sp) //self assignment
{
/*if(r->release() == 0)
{
delete p;
delete r;
}*/ //this will cause an error when you take something with no references and set it equal to something
p = sp.p;
r = sp.r;
r->add();
}
return *this;
}
};
node.h:
#pragma once
template <class type>
struct node
{
type data;
node *next;
node()
{
next = NULL;
}
};
The line that specifically throws "Cannot read from 0xfdfdfe01" from the if statement in the linked list's goToFromFront(int), where, at the point j = 2 in the main loop the error is thrown. Upon looking at the MSVS2010 debugger, temp->next is unknown (CXX0030: error, expression cannot be evaluated), which to me seems like it should translate to null, but the expression is throwing a cannot be read error first.
I'm not honestly sure what I've done wrong, and as this is all a learning process for me, any critique is highly appreciated. Thanks in advance!
These should fix your issues:
uncomment code in operator= of sPtr or use swap idiom:
sPtr<type>& operator=(const sPtr<type>& rhs)
{
if (this != &rhs) // self assignment
{
sPtr<type> tmp(rhs);
std::swap(this->p, tmp.p);
std::swap(this->r, tmp.r);
}
return *this;
}
template <class T>
class node
{
public:
T data;
sPtr<node<T> > next;
};
bool insertAtFront(type obj)
{
sPtr<node<type>> temp(new node<type>);
temp->data = obj;
temp->next = head;
head = temp;
size++;
return true;
}
in goToFromFront, 'temp = temp->next;' created a refCount with one usage of temp->next.
when 'temp' goes out of scope, it destroys its content, and so the 'head->next' points to garbage.
When you do sPtr > = T* , you create a temporary object implicitly
you may declare as sTtr constructor as:
explicit sPtr(type *pValue)