c++ - Do I need to set my destructor methods when I am using shared pointers? -
i tried finding answer didn't see 1 particular problem. using shared pointers ternary search tree (to used predictive text algorithm) , running problems using shared pointers.
i've been away c++ 5 years, , let me tell you, java not learn pointers. i've had relearn pointer material learned in school 5-6 years ago on past couple of days, , have managed destroy code.
here of code 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 copy of node swap new 1 //do first 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 string 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 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 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 node while traversing //base case there no root node if (!root_node_) { for(std::vector<char>::iterator = characters.begin(); != characters.end(); ++it) { if (*it != '\0') { //if first character //root_node_ equal 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, need compare each character } else { currentnode = root_node_; for(std::vector<char>::iterator = characters.begin(); != characters.end(); ++it) { if (*it != '\0') { currentnode.reset(&setnextnode(*currentnode, *it, *std::next(it, 1))); } else { currentnode->set_end_of_word(true); } } } } //recursive function obtaining/adding next node when inserting 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 new node , recall method on node } else { //if not end of word, make new node next letter if (nextchar != '\0') { return insertnode(currentnode, nextchar, node_pos_centre, false); } else { return currentnode; } } //if less, follow node on 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 new node , recall method on node } else { return setnextnode(insertnode(currentnode, currentchar, node_pos_left, false), currentchar, nextchar); } //otherwise bigger, 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 new node , recall method on node } else { return setnextnode(insertnode(currentnode, currentchar, node_pos_right, false), currentchar, nextchar); } } } //populate tst 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 1 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 @ root , follow paths std::cout << tst.get_root_node(); /**std::vector<char> vec; vec.push_back('a'); vec.push_back('c'); std::vector<char>::iterator = 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 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 stream char c = obj.get_character(); bool b = obj.is_end_of_word(); os << c << "\t end of word: " << b; return os; }
when run in debug mode (visual studios), able set root node, when goes input second node, crashes trying delete "stuff" when currentnode calls .reset(&node) within else statement of function insertword. doing wrong in copy constructors or operator= methods, or destructors? cout line above print correct letter, looks node 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 incorrect and/or missing, no symbols loaded kernel32.dll]
ntdll.dll!77599f72() unknown ntdll.dll!77599f45() unknown
thanks can provide! , let me know if there anythign else need me provide (the text file reading in has word corn
in it).
your problem you're using java style in c++. unlike in java pointer, in c++ have think difference between values, references, pointers, , object lifetime.
this function 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 taking ternarysearchtreenode
objects value, putting address shared_ptr
. point of shared_ptr
to take ownership of dynamically allocated object (one created using new
) , delete when reference count goes zero. objects above (left_node, etc) stack objects go out of scope @ end of function. when put address shared_ptr
, try delete objects later, no longer exist.
as far recommending how fix this, there whole lot going on here assumptions off. instance, can child node have more 1 parent? make sense copy nodes?
i'll assume moment copying nodes makes sense, using shared_ptr
reasonable. in case 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 of functions set_left_node
should take std::shared_ptr<ternarysearchnode>
parameters. should not calling reset()
, exists allow shared_ptr
take ownership (refcount == 1) of free pointer. shared_ptr
works incrementing reference count on copy , dereferencing in destructor. when dereference pointer , take address, working around shared_ptr.
Comments
Post a Comment