I am a beginner at C++ and am have some issues with 2 separate errors. Unable to access memory and stack overflow.
This is my implementation for a Trie Tree, using pointers, of words containing characters a-z. When running tests, I can successfully add several hundred, or even thousands of nodes without issue, until it eventually crashes. Error: Unable to access memory. I more often get this error when I am trying to run a query and use the "isAWord" function. I also get a stack overflow when I try to run the deconstructor. Any help is appreciate, as I've spent 2 days trying to debug with little success.
#include "Trie.h"
#include <iostream>
#include <iterator>
#include <sstream>
using namespace std;
//sets up tree
Trie::Trie()
{
for (int i = 0; i < ALPH; i++)
this->childs[i] = nullptr;
endNode = false;
}
//add 'userInput' string to trie
void Trie::addAWord(std::string userInput)
{
Trie* start = this;
for (int i = 0; i < userInput.length(); i++)
{
int index = userInput[i] - 'a';
if (start->childs[index] == nullptr)
start->childs[index] = new Trie();
start = start->childs[index];
}
start->endNode = true;
}
//returns true if 'wordFind' is in tree
bool Trie::isAWord(std::string wordFind)
{
if (this == nullptr)
return false;
Trie* start = this;
for (int i = 0; i < wordFind.length(); i++)
{
int index = wordFind[i] - 'a';
start = start->childs[index];
if (start == nullptr)
return false;
}
return start->endNode;
}
//returns a vector containing the words in tree with prefix 'prefFind'
vector<std::string> Trie::allWordsStartingWithPrefix(std::string prefFind)
{
string pres = PrefixRec(prefFind,*this);
stringstream preStream(pres);
istream_iterator<std::string> begin(preStream), end;
vector<std::string> stringSet(begin, end);
copy(stringSet.begin(), stringSet.end(), std::ostream_iterator<std::string>(std::cout, "\n"));
return stringSet;
}
//helper method for AllWordsStartingWithPrefix
std::string Trie::PrefixRec(std::string& key, Trie const temp)
{
if (temp.endNode)
return(key + " ");
for (char index = 0; index < ALPH; ++index)
{
index = key[index] - 'a';
Trie const* curChild = temp.childs[index];
if (curChild)
{
key.push_back(index);
PrefixRec(key, *curChild);
key.pop_back();
}
}
}
//copy cons and assignment op
Trie& Trie::operator=(const Trie& other)
{
Trie* newPtr = new Trie(other);
other.~Trie();
return *newPtr;
}
//deconstructor
Trie::~Trie()
{
if (this == nullptr)
return;
for (int i = 0; i < ALPH; i++)
{
if (childs[i] != nullptr)
childs[i]->~Trie();
}
delete this;
return;
}
#include <iostream>
#include <vector>
#include <string>
#define ALPH 26
class Trie
{
public:
bool endNode;
Trie* childs[ALPH];
Trie();
void addAWord(std::string key);
bool isAWord(std::string key);
std::vector<std::string> allWordsStartingWithPrefix(std::string key);
Trie& operator=(const Trie& other);
std::vector<std::string> wordsWithWildcardPrefix(std::string);
std::string PrefixRec(std::string& key, Trie const temp);
~Trie();
};
I also get a stack overflow when I try to run the deconstructor.
This is because of this line:
delete this;
This is what a delete does
The delete expression invokes the destructor (if any) for the object
that's being destroyed,
You can imagine why calling delete from within the destructor would be problematic. (Hint: Infinite recursion)
You don't want any delete this in your code.
Once you get rid of this, there are other issues.(Although you may live just by fixing this). For instance calling the destructor explicitly as you are doing in this line(and several other lines)
other.~Trie();
From iso cpp:
Should I explicitly call a destructor on a local variable?
No!
The destructor will get called again at the close } of the block in which the local was created. This is a guarantee of the language; it happens automagically; there’s no way to stop it from happening. But you can get really bad results from calling a destructor on the same object a second time! Bang! You’re dead!
Replace the explicit destructor calls with delete and let it call the destructor correctly.
I would recommend replace any raw pointers and new and delete with smart pointer. Start with shared_ptr to begin with. (raw_pointers are so 2010 ;))
Footnote: Get rid of these checks. They are non-idiomatic. It's ok and desirable for the caller to burn when calling a member function on a nullptr
if (this == nullptr)
return false;
Related
TrieNode and Trie Object:
struct TrieNode {
char nodeChar = NULL;
map<char, TrieNode> children;
TrieNode() {}
TrieNode(char c) { nodeChar = c; }
};
struct Trie {
TrieNode *root = new TrieNode();
typedef pair<char, TrieNode> letter;
typedef map<char, TrieNode>::iterator it;
Trie(vector<string> dictionary) {
for (int i = 0; i < dictionary.size(); i++) {
insert(dictionary[i]);
}
}
void insert(string toInsert) {
TrieNode * curr = root;
int increment = 0;
// while letters still exist within the trie traverse through the trie
while (curr->children.find(toInsert[increment]) != curr->children.end()) { //letter found
curr = &(curr->children.find(toInsert[increment])->second);
increment++;
}
//when it doesn't exist we know that this will be a new branch
for (int i = increment; i < toInsert.length(); i++) {
TrieNode temp(toInsert[i]);
curr->children.insert(letter(toInsert[i], temp));
curr = &(curr->children.find(toInsert[i])->second);
if (i == toInsert.length() - 1) {
temp.nodeChar = NULL;
curr->children.insert(letter(NULL, temp));
}
}
}
vector<string> findPre(string pre) {
vector<string> list;
TrieNode * curr = root;
/*First find if the pre actually exist*/
for (int i = 0; i < pre.length(); i++) {
if (curr->children.find(pre[i]) == curr->children.end()) { //DNE
return list;
}
else {
curr = &(curr->children.find(pre[i])->second);
}
}
/*Now curr is at the end of the prefix, now we will perform a DFS*/
pre = pre.substr(0, pre.length() - 1);
findPre(list, curr, pre);
}
void findPre(vector<string> &list, TrieNode *curr, string prefix) {
if (curr->nodeChar == NULL) {
list.push_back(prefix);
return;
}
else {
prefix += curr->nodeChar;
for (it i = curr->children.begin(); i != curr->children.end(); i++) {
findPre(list, &i->second, prefix);
}
}
}
};
The problem is this function:
void findPre(vector<string> &list, TrieNode *curr, string prefix) {
/*if children of TrieNode contains NULL char, it means this branch up to this point is a complete word*/
if (curr->nodeChar == NULL) {
list.push_back(prefix);
}
else {
prefix += curr->nodeChar;
for (it i = curr->children.begin(); i != curr->children.end(); i++) {
findPre(list, &i->second, prefix);
}
}
}
The purpose is to return all words with the same prefix from a trie using DFS. I manage to retrieve all the necessary strings but I can't exit out of the recursion.
The code completes the last iteration of the if statement and breaks. Visual Studio doesn't return any error code.
The typical end to a recursion is just as you said- return all words. A standard recursion looks something like this:
returnType function(params...){
//Do stuff
if(need to recurse){
return function(next params...);
}else{ //This should be your defined base-case
return base-case;
}
The issue arises in that your recursive function can never return- it can either execute the push_back, or it can call itself again. Neither of these seems to properly exit, so it'll either end quietly (with an inferred return of nothing), or it'll keep recursing.
In your situation, you likely need to store the results from recursion in an intermediate structure like a list or such, and then return that list after iteration (since it's a tree search and ought to check all the children, not return the first one only)
On that note, you seem to be missing part of the point of recursions- they exist to fill a purpose: break down a problem into pieces until those pieces are trivial to solve. Then return that case and build back to a full solution. Any tree-searching must come from this base structure, or you may miss something- like forgetting to return your results.
Check the integrity of your Trie structure. The function appears to be correct. The reason why it wouldn't terminate is if one or more of your leaf nodes doesn't have curr->nodeChar == NULL.
Another case is that any node (leaf or non-leaf) has a garbage child node. This will cause the recursion to break into reading garbage values and no reason to stop. Running in debug mode should break the execution with segmentation fault.
Write another function to test if all leaf-nodes have NULL termination.
EDIT:
After posting the code, the original poster has already pointed out that the problem was that he/she was not returning the list of strings.
Apart from that, there are a few more suggestions I would like to provide based on the code:
How does this while loop terminate if toInsert string is already in the Trie.
You will overrun the toInsert string and read a garbage character.
It will exit after that, but reading beyond your string is a bad way to program.
// while letters still exist within the trie traverse through the trie
while (curr->children.find(toInsert[increment]) != curr->children.end())
{ //letter found
curr = &(curr->children.find(toInsert[increment])->second);
increment++;
}
This can be written as follows:
while (increment < toInsert.length() &&
curr->children.find(toInsert[increment]) != curr->children.end())
Also,
Trie( vector<string> dictionary)
should be
Trie( const vector<string>& dictionary )
because dictionary can be a large object. If you don't pass by reference, it will create a second copy. This is not efficient.
I am a idiot. I forgot to return list on the first findPre() function.
vector<string> findPre(string pre) {
vector<string> list;
TrieNode * curr = root;
/*First find if the pre actually exist*/
for (int i = 0; i < pre.length(); i++) {
if (curr->children.find(pre[i]) == curr->children.end()) { //DNE
return list;
}
else {
curr = &(curr->children.find(pre[i])->second);
}
}
/*Now curr is at the end of the prefix, now we will perform a DFS*/
pre = pre.substr(0, pre.length() - 1);
findPre(list, curr, pre);
return list; //<----- this thing
}
Bear with me, I'm new to C++. I'm trying to update a value which is stored in a vector, but I'm getting this error:
non-const lvalue reference to type 'Node'
I'm using a simple wrapper around std::vector so I can share methods like contains and others (similar to how the ArrayList is in Java).
#include <vector>
using namespace std;
template <class T> class NewFrames {
public:
// truncated ...
bool contains(T data) {
for(int i = 0; i < this->vec->size(); i++) {
if(this->vec->at(i) == data) {
return true;
}
}
return false;
}
int indexOf(T data) {
for(int i = 0; i < this->vec->size(); i++) {
if(this->vec->at(i) == data) {
return i;
}
}
return -1;
}
T get(int index) {
if(index > this->vec->size()) {
throw std::out_of_range("Cannot get index that exceeds the capacity");
}
return this->vec->at(index);
}
private:
vector<T> *vec;
};
#endif // A2_NEWFRAMES_H
The class which utilizes this wrapper is defined as follows:
#include "Page.h"
#include "NewFrames.h"
class Algo {
private:
typedef struct Node {
unsigned reference:1;
int data;
unsigned long _time;
Node() { }
Node(int data) {
this->data = data;
this->reference = 0;
this->_time = (unsigned long) time(NULL);
}
} Node;
unsigned _faults;
Page page;
NewFrames<Node> *frames;
};
I'm at a point where I need to reference one of the Node objects inside of the vector, but I need to be able to change reference to a different value. From what I've found on SO, I need to do this:
const Node &n = this->frames->get(this->frames->indexOf(data));
I've tried just using:
Node n = this->frames->get(this->frames->indexOf(data));
n.reference = 1;
and then viewing the data in the debugger, but the value is not updated when I check later on. Consider this:
const int data = this->page.pages[i];
const bool contains = this->frames->contains(Node(data));
Node node = this->frames->get(index);
for(unsigned i = 0; i < this->page.pages.size(); i++) {
if(node == NULL && !contains) {
// add node
} else if(contains) {
Node n = this->frames->get(this->frames->indexOf(data));
if(n.reference == 0) {
n.reference = 1;
} else {
n.reference = 0;
}
} else {
// do other stuff
}
}
With subsequent passes of the loop, the node with that particular data value is somehow different.
But if I attempt to change n.reference, I'll get an error because const is preventing the object from changing. Is there a way I can get this node so I can change it? I'm coming from the friendly Java world where something like this would work, but I want to know/understand why this doesn't work in C++.
Node n = this->frames->get(this->frames->indexOf(data));
n.reference = 1;
This copies the Node from frames and stores the copy as the object n. Modifying the copy does not change the original node.
The simplest "fix" is to use a reference. That means changing the return type of get from T to T&, and changing the previous two lines to
Node& n = this->frames->get(this->frames->indexOf(data));
n.reference = 1;
That should get the code to work. But there is so much indirection in the code that there are likely to be other problems that haven't shown up yet. As #nwp said in a comment, using vector<T> instead of vector<T>* will save you many headaches.
And while I'm giving style advice, get rid of those this->s; they're just noise. And simplify the belt-and-suspenders validity checks: when you loop from 0 to vec.size() you don't need to check that the index is okay when you access the element; change vec.at(i) to vec[i]. And in get, note that vec.at(index) will throw an exception if index is out of bounds, so you can either skip the initial range check or keep the check (after fixing it so that it checks the actual range) and, again, use vec[index] instead of vec.at(index).
I tried finding an answer but didn't see one for my particular problem. I am using shared pointers for a ternary search tree (to be used for a predictive text algorithm) and am running into some problems using shared pointers.
I've been away from C++ for 5 years, and let me tell you, Java does not help you learn pointers. I've had to relearn pointer material I learned in school 5-6 years ago over the past couple of days, and have successfully managed to destroy my code.
Here is most of the code I have:
// TernarySearchTree.cc
#include "stdafx.h"
#include "ternary_search_tree.h"
//Constructor
TernarySearchTree::TernarySearchTree() {
num_nodes_ = 0;
size_in_memory_ = 0;
root_node_ = nullptr;
}
TernarySearchTree::TernarySearchTree(const TernarySearchTree& other) {
num_nodes_ = other.num_nodes_;
size_in_memory_ = other.size_in_memory_;
TernarySearchTreeNode node;
node = *other.root_node_;
root_node_.reset(&node);
}
//Destructor
TernarySearchTree::~TernarySearchTree() {
}
//operators
TernarySearchTree& TernarySearchTree::operator=(const TernarySearchTree& other) {
//TODO: swap idiom - create a copy of the node then swap the new one with it
//do this first to provide exception safety
TernarySearchTreeNode node;
node = *other.root_node_;
root_node_.reset(&node);
num_nodes_ = other.num_nodes_;
size_in_memory_ = other.size_in_memory_;
return *this;
}
//Convert from string to c-style string
std::vector<char> TernarySearchTree::ConvertStringToCString(std::string str) {
std::vector<char> wordCharacters (str.begin(), str.end());
//remove newlines or tabs
if (wordCharacters.back() == '\n' || wordCharacters.back() == '\t') {
wordCharacters.pop_back();
}
wordCharacters.push_back('\0');
return wordCharacters;
}
//Insert a node
TernarySearchTreeNode TernarySearchTree::InsertNode(TernarySearchTreeNode ¤tNode,
char character,
NodePosition position,
bool isRoot) {
TernarySearchTreeNode newNode;
newNode.set_character(character);
if (!isRoot) {
switch (position) {
case NODE_POS_LEFT:
currentNode.set_left_node(newNode);
break;
case NODE_POS_CENTRE:
currentNode.set_centre_node(newNode);
break;
case NODE_POS_RIGHT:
currentNode.set_right_node(newNode);
break;
default:
break;
}
}
return newNode;
}
//Insert a word
void TernarySearchTree::InsertWord(std::string word) {
std::vector<char> characters = ConvertStringToCString(word);
std::shared_ptr<TernarySearchTreeNode> currentNode = 0;
bool isFirstCharacter = true;
//Add each character to a node while traversing
//Base case where there is no root node
if (!root_node_) {
for(std::vector<char>::iterator it = characters.begin(); it != characters.end(); ++it) {
if (*it != '\0') {
//if it is the first character
//root_node_ is equal to the address of new node
if (isFirstCharacter) {
std::cout << "HIHI";
TernarySearchTreeNode node = InsertNode(*currentNode, *it, NODE_POS_CENTRE, true);
root_node_.reset(&node);
currentNode.reset(&node);
isFirstCharacter = false;
} else {
TernarySearchTreeNode node = InsertNode(*currentNode, *it, NODE_POS_CENTRE, false);
std::cout << std::endl << node.get_character();
currentNode.reset(&node);
}
}
}
//If not base case, then we need to compare each character
} else {
currentNode = root_node_;
for(std::vector<char>::iterator it = characters.begin(); it != characters.end(); ++it) {
if (*it != '\0') {
currentNode.reset(&SetNextNode(*currentNode, *it, *std::next(it, 1)));
} else {
currentNode->set_end_of_word(true);
}
}
}
}
//Recursive function for obtaining/adding the next node when inserting a word
TernarySearchTreeNode TernarySearchTree::SetNextNode(TernarySearchTreeNode ¤tNode, const char currentChar, const char nextChar) {
//If characters match
if (currentChar == currentNode.get_character()) {
//if centre node exists
if (currentNode.get_centre_node()) {
return *(currentNode.get_centre_node());
//Otherwise, create a new node and recall method on that node
} else {
//If not the end of the word, make a new node with the next letter
if (nextChar != '\0') {
return InsertNode(currentNode, nextChar, NODE_POS_CENTRE, false);
} else {
return currentNode;
}
}
//If it is less, follow node on the left
} else if (currentChar < currentNode.get_character()) {
//if left node exists, recursive call
if (currentNode.get_left_node()) {
return SetNextNode(*(currentNode.get_left_node()), currentChar, nextChar);
//Otherwise, create a new node and recall method on that node
} else {
return SetNextNode(InsertNode(currentNode, currentChar, NODE_POS_LEFT, false), currentChar, nextChar);
}
//Otherwise it is bigger, so take right path
} else {
//if right node exists, recursive call
if (currentNode.get_right_node()) {
return SetNextNode(*(currentNode.get_right_node()), currentChar, nextChar);
//Otherwise, create a new node and recall method on that node
} else {
return SetNextNode(InsertNode(currentNode, currentChar, NODE_POS_RIGHT, false), currentChar, nextChar);
}
}
}
//Populate the TST from a word list/file
void TernarySearchTree::PopulateTreeFromTextFile(std::string fileName) {
std::ifstream file;
std::string line;
file.open(fileName);
if (file.is_open()) {
//Assume text file has one word per line
while (std::getline(file, line)) {
InsertWord(line);
}
}
}
//Search
bool TernarySearchTree::SearchForWord(std::string word) {
return false;
}
int _tmain(int argc, _TCHAR* argv[])
{
//Test
TernarySearchTree tst;
//Open file
tst.PopulateTreeFromTextFile("simple.txt");
//start at root and follow some paths
std::cout << tst.get_root_node();
/**std::vector<char> vec;
vec.push_back('a');
vec.push_back('c');
std::vector<char>::iterator it = vec.begin();
std::cout << *std::next(vec.begin(), 1);
std::cout << (*it < 'c');
it++;
std::cout << *std::next(it, 0);
std::cout << (*it < 'c');
**/
return 0;
}
and for the nodes:
/*TST node methods */
#include <iostream>
#include "ternary_search_tree_node.h"
/** ADD COPY CONSTRUCTOR*/
//Constructors
TernarySearchTreeNode::TernarySearchTreeNode() {
character_ = '\0';
end_of_word_ = false;
left_node_ = nullptr;
centre_node_ = nullptr;
right_node_ = nullptr;
}
TernarySearchTreeNode::TernarySearchTreeNode(const TernarySearchTreeNode& other) {
character_ = other.character_;
end_of_word_ = other.end_of_word_;
TernarySearchTreeNode leftNode;
leftNode = *other.left_node_;
left_node_.reset(&leftNode);
TernarySearchTreeNode centreNode;
centreNode = *other.centre_node_;
centre_node_.reset(¢reNode);
TernarySearchTreeNode rightNode;
rightNode = *other.right_node_;
right_node_.reset(&rightNode);
}
TernarySearchTreeNode::TernarySearchTreeNode(char character, bool end_of_word,
TernarySearchTreeNode left_node,
TernarySearchTreeNode centre_node,
TernarySearchTreeNode right_node) {
character_ = character;
end_of_word_ = end_of_word;
left_node_.reset(&left_node);
centre_node_.reset(¢re_node);
right_node_.reset(&right_node);
}
//Destructor
TernarySearchTreeNode::~TernarySearchTreeNode() {
left_node_.reset();
centre_node_.reset();
right_node_.reset();
}
//operators
TernarySearchTreeNode& TernarySearchTreeNode::operator=(const TernarySearchTreeNode& other) {
if (&other) {
TernarySearchTreeNode leftNode;
leftNode = *other.left_node_;
TernarySearchTreeNode centreNode;
centreNode = *other.centre_node_;
TernarySearchTreeNode rightNode;
rightNode = *other.right_node_;
left_node_.reset(&leftNode);
centre_node_.reset(¢reNode);
right_node_.reset(&rightNode);
character_ = other.character_;
end_of_word_ = other.end_of_word_;
}
return *this;
}
//printing
std::ostream& operator<<(std::ostream& os, const TernarySearchTreeNode& obj)
{
// write obj to stream
char c = obj.get_character();
bool b = obj.is_end_of_word();
os << c << "\t is end of word: " << b;
return os;
}
When I run in debug mode (Visual Studios), it is able to set the root node, but when it goes to input the second node, it crashes trying to delete "stuff" when currentNode calls .reset(&node) within the else statement of function InsertWord. Am I doing something wrong in the copy constructors or operator= methods, or the destructors? The cout line above it does print the correct letter, so it looks like the node is getting created properly.
The debug call stack shows:
TernarySearchTree.exe!std::_Ref_count_base::_Decref() Line 118 C++
TernarySearchTree.exe!std::_Ptr_base::_Decref()
Line 347 C++
TernarySearchTree.exe!std::shared_ptr::~shared_ptr()
Line 624 C++
TernarySearchTree.exe!std::shared_ptr::reset()
Line 649 C++
TernarySearchTree.exe!TernarySearchTreeNode::~TernarySearchTreeNode()
Line 50 C++ TernarySearchTree.exe!TernarySearchTreeNode::`scalar
deleting destructor'(unsigned int) C++
TernarySearchTree.exe!std::_Ref_count::_Destroy()
Line 161 C++ TernarySearchTree.exe!std::_Ref_count_base::_Decref()
Line 120 C++
TernarySearchTree.exe!std::_Ptr_base::_Decref()
Line 347 C++
TernarySearchTree.exe!std::shared_ptr::~shared_ptr()
Line 624 C++
TernarySearchTree.exe!std::shared_ptr::reset()
Line 649 C++
TernarySearchTree.exe!TernarySearchTreeNode::~TernarySearchTreeNode()
Line 50 C++
TernarySearchTree.exe!TernarySearchTree::InsertWord(std::basic_string,std::allocator
word) Line 105 C++ TernarySearchTree.exe!TernarySearchTree::PopulateTreeFromTextFile(std::basic_string,std::allocator
fileName) Line 182 C++ TernarySearchTree.exe!wmain(int argc, wchar_t * * argv) Line 200 C++
TernarySearchTree.exe!__tmainCRTStartup() Line 533 C
TernarySearchTree.exe!wmainCRTStartup() Line 377 C
kernel32.dll!7592338a() Unknown [Frames below may be incorrect
and/or missing, no symbols loaded for kernel32.dll]
ntdll.dll!77599f72() Unknown ntdll.dll!77599f45() Unknown
Thanks for any help you can provide! And let me know if there is anythign else you need me to provide (the text file I am reading in just has the word cornin it).
Your problem is that you're using Java style in C++. Unlike in Java where everything is essentially a pointer, in C++ you have to think about the difference between values, references, pointers, and object lifetime.
This function is bad:
TernarySearchTreeNode::TernarySearchTreeNode(char character, bool end_of_word,
TernarySearchTreeNode left_node,
TernarySearchTreeNode centre_node,
TernarySearchTreeNode right_node) {
character_ = character;
end_of_word_ = end_of_word;
left_node_.reset(&left_node);
centre_node_.reset(¢re_node);
right_node_.reset(&right_node);
}
You are taking TernarySearchTreeNode objects by value, then putting their address into a shared_ptr. The point of a shared_ptr to to take ownership of a dynamically allocated object (one created using new) and delete it when the reference count goes to zero. The objects above (left_node, etc) are stack objects that will go out of scope at the end of the function. When you put their address into a shared_ptr, it will then try to delete those objects later, but they no longer exist.
As far as recommending how to fix this, there is a whole lot going on here where the assumptions are just off. For instance, can a child node have more than one parent? Does it actually make sense to copy nodes?
I'll assume for the moment that copying nodes makes sense, so using shared_ptr is reasonable. In that case we might start here:
TernarySearchTreeNode TernarySearchTree::InsertNode(std::shared_ptr<TernarySearchTreeNode currentNode>,
char character,
NodePosition position,
bool isRoot) {
auto newNode = std::make_shared<TernarySearchTreeNode>();
newNode->set_character(character);
if (!isRoot) {
switch (position) {
case NODE_POS_LEFT:
currentNode->set_left_node(newNode);
Then all of your functions like set_left_node should also take std::shared_ptr<TernarySearchNode> as parameters. You should not be calling reset(), which exists to allow a shared_ptr to take ownership (refcount == 1) of a free pointer. shared_ptr works by incrementing the reference count on copy and dereferencing in the destructor. When you dereference the pointer and then take the address, you are working around the shared_ptr.
For the life of me, I can't figure out what is going wrong. I know the error is occurring in the function marked displayQueue below, but all the syntax and logic seems correct.
Visual studio is giving me the error: "Unhandled exception at 0x00215A86 in ex11_1.exe: 0xC0000005: Access violation reading location 0xCDCDCDE1." But really, I have no idea what this is referring to...
#include <iostream>
#include <cstdlib>
#include <string>
using namespace std;
struct QueueNode {
string data;
QueueNode *link;
};
typedef QueueNode* QueueNodePtr;
class Queue {
public:
// Constructors/Destructor.
Queue();
Queue(const Queue& aQueue);
// Accessors.
bool empty() const;
void displayQueue ();
// Mutators.
void add(string item);
string remove(); // This should probably be replaced with pop and top - especially for displayQueue... empty() in functions can be replaced with count == 0. Yes, I will do this.
private:
QueueNodePtr front; // Points to head of linked-list queue.
QueueNodePtr back; // Points to tail of linked-list queue.
size_t count;
};
int main () {
Queue myQueue;
myQueue.add("abc");
myQueue.add("def");
myQueue.add("ghi");
myQueue.displayQueue(); // The error is here somewhere. abc is printed and but nothing else.
system("pause");
return 0;
}
Queue::Queue() {
front = NULL;
back = NULL;
count = 0;
}
Queue::Queue(const Queue& aQueue) {
front = aQueue.front;
back = aQueue.back;
count = aQueue.count;
}
bool Queue::empty() const {
if (count == 0) {
return 1;
} else {
return 0;
}
}
void Queue::displayQueue () {
// There is a problem here somewhere...
QueueNodePtr here = front;
for (int i = 0; i < count; i++) {
cout << here->data << endl;
here = here->link;
}
}
void Queue::add(string item) {
QueueNodePtr newNode;
newNode = new QueueNode;
if (count == 0) {
// If inserted in an empty queue, back and front point to same element.
newNode->data = item;
// newNode->link = NULL; // Not sure this part is even necessary.
back = newNode;
front = back;
} else {
// Otherwise, leave front pointer where it's at.
newNode->data = item;
newNode->link = back->link;
back = newNode;
}
count ++;
}
string Queue::remove() {
string returnString;
if (count == 0) {
return returnString;
} else if (count == 1) {
returnString = front->data;
front = NULL;
back = front;
count--;
return returnString;
} else {
returnString = front->data;
front = front->link;
count--;
return returnString;
}
}
EDIT: If anyone can give me any tips on using the debugger to solve problems like this, or give me a link that might explain this it would be greatly appreciated.
The error is on this line, but for the sake of learning, I won't give the correct version, just a few hints:
newNode->link = back->link;
At the point where this code is being executed, which node does back point to? What does its link point to? Whose node's link do you need to modify?
As for finding this yourself, you could have used the debugger to figure out which line causes the crash; this would have indicated that something is wrong with a link value.
P.S. Your copy constructor doesn't actually copy the linked list; it just creates a new Queue object that points to the same linked list, so if you add an element to the copy, it will show up in the original Queue.
An access violation at address 0xCDCDCDCD means that your program loaded a pointer from uninitialized storage and then dereferenced it. Microsoft's debugging allocator uses this pattern for newly allocated uninitialized storage, and in a suitable compilation mode, also for stack locations. If you treat such uninitialized storage as a pointer variable, the pattern is recognizeable in that pointer. Moreover, it is almost certainly an invalid pointer that will trigger an exception. So the benefit is that the use of the invalid pointer is caught quickly, and the pattern tells you that the cause is quite likely uninitialized storage (though this is not 100% conclusive).
For example:
struct contains_pointer { char *str; } *ptr = malloc(sizeof *ptr);
strcpy(ptr->str, "abc"); // ptr->str is uninitialized
Or:
int *pint;
*pint = 0; // pint is uninitialized
To have the compiler and library overwrite uninitialized storage with a pattern like CDCDCD... can be quite helpful. You should pinpoint the location of the crash with the debugger, and then work backward from there: where did the pointer value originate and why wasn't it initialized.
(A bad pointer to the address CDCDCDCD could result in other ways: sheer fluke (unlikely) or a use-after-free bug: the program frees some memory but continues to keep a pointer to it, without using it for a while. The memory is then re-allocated to some other part of the program, and marked uninitialized, and by chance, the original user of the pointer makes a use of it, loading a pointer value from the memory. At that moment, a CDCDCDCD pointer results, so it looks like a use-before-init bug, when in fact it's a use-after-free bug. Debugging based on "memory poisoning" patterns is not accurate!)
I am getting a very strange error in my code. This assignment is for a class I'm taking and essentially we are learning how to implement a hash table. The error i'm getting is when I try and rehash to a larger size. Here's the portion of the code giving me the problem, and I'll explain more fully what the problem is.
if(htable->size>=htable->cap)
{
cout<<htable->cap<<endl;
HashTable tempht=*htable;
delete htable;
htable=new HashTable((tempht.cap * 2) + 1);
for (size_t i=0; i<tempht.cap; i++)
{
Node* n=tempht.table[i];
while (n!=NULL)
{
htable->add(n->item);
n=n->next;
}
}
if (htable->table[0]==NULL)
{
cout<<"HOORAY!"<<endl;
}
}
if (htable->table[0]==NULL)
{
cout<<"HOORAY!"<<endl;
}
else
{
cout<<htable->table[0]->item<<endl;
}
htable is a HashTable variable. In the HashTable class it contains an array Node* (Nodes are just objects I created that contain a string and a pointer to the next item in the chain). This part of the code is simply trying to rehash to a larger table. The issue I'm getting is once I exit the first if statement, my table's first value no longer equals NULL (the test I'm running rehashes a table with nothing in it to a table that still has nothing in it, but has a larger capacity). When I run the code, the first htable->table[0]==NULL passes while the second does not, despite there being no changes other than exiting the if statement (my expected result is that the table[0] should be NULL). My best guess is it's some kind of scoping error, but I honestly can't see where the problem is. Any help would be greatly appreciated.
Edit: Just to clarify, the initial hash table has a capacity of 0 (this is one of the project requirements). So when i try to add an item to the table, this if statement is executed (since the size is 0 and the cap is 0, we have to maintain a load factor of 1). I can confirm that once the table reaches the first and second "Hooray" checks, that htable->cap (which is the total capacity of the array) is 1, which is what it should be. The only thing that is getting messed is bucket 0 (which in this case is the only bucket). For whatever reason, it's null before exiting the if statement but not after.
I'm posting my whole HashTable class, let me know if you find anything.
#pragma once
#include <iostream>
#include <string>
#include <fstream>
#include "Node.h"
using namespace std;
class HashTable
{
public:
Node** table;
int size;
int cap;
HashTable (int c)
{
size=0;
cap=c;
table = new Node*[cap];
if (cap>0)
{
for (size_t i=0; i<cap; ++i)
{
table[i]=NULL;
}
}
}
~HashTable()
{
delete table;
}
size_t hash(string thing)
{
size_t total=0;
int asci;
char c;
size_t index;
for (size_t i=0; i<thing.length(); i++)
{
total=total*31;
c=thing[i];
asci=int(c);
total=asci+total;
}
index=total%cap;
cout<<"index"<<index<<endl;
system("pause");
return index;
}
void add(string thing)
{
size_t index;
index=hash(thing);
cout<<"index "<<index<<endl;
system("pause");
Node* temp=table[index];
if (temp==NULL)
{
cout<<"Here"<<endl;
system("pause");
}
else
{
cout<<"Here2"<<endl;
system("pause");
cout<<"temp"<<temp->item<<endl;
system("pause");
}
Node* n = new Node(thing);
cout<<"n"<<n->item<<endl;
system("pause");
if (temp==NULL)
{
table[index]=n;
}
else
{
while (temp->next!=NULL)
{
temp=temp->next;
}
temp->next=n;
}
size++;
}
Node* find(string search)
{
Node* n= NULL;
size_t index;
if(cap!=0)
{
index=hash(search);
Node* temp=table[index];
while (temp!=NULL)
{
if (temp->item==search)
{
n=temp;
return n;
}
}
}
return n;
}
void remove (string thing)
{
if (find(thing)==NULL)
{
return;
}
else
{
size_t index;
index=hash(thing);
Node* temp=table[index];
if (temp->item==thing)
{
table[index]=temp->next;
delete temp;
}
while (temp->next!=NULL)
{
if (temp->next->item==thing)
{
Node* temp2=temp->next;
temp->next=temp->next->next;
delete temp2;
break;
}
}
}
size--;
}
void print(ofstream &ofile)
{
for (size_t i=0; i<cap; i++)
{
Node* n=table[i];
ofile<<"hash "<<i<<":";
while (n!=NULL)
{
ofile<<" "<<n->item;
n=n->next;
}
}
}
};
Well, this is C++, and I'm more a Java guy, but I'll take a stab at it.
Turns out the problem IS with the
HashTable tempht=*htable;
delete htable;
block after all.
See, the first line there says "copy all of the members from *htable into tempht". So now tempht and htable SHARE their table memory, since table is just a pointer to memory that was allocated at construction, and you just copied the pointer. You wanted it to copy the nodes inside table, but it didn't do that.
So now you have two different HashTable objects with the same pointer value in table. Now, when tempht is freed, the destructor calls free on the table pointer, which effectively frees the table data in both objects htable and tempht.
What you really want to do is write a copy constructor, or do something like:
HashTable *tempht=htable;
htable=new HashTable((tempht->cap * 2) + 1);
for (size_t i=0; i<tempht->cap; i++)
{
Node* n=tempht->table[i];
while (n!=NULL)
{
htable->add(n->item);
n=n->next;
}
}
if (htable->table[0]==NULL)
{
cout<<"HOORAY!"<<endl;
}
delete tempht;
See how all I've really done is change tempht to a pointer, using it to point to the old hashtable while you copy all the nodes from it to the new htable object, then deleting the old Hashtable.