C++ Reference Issue - c++

I recently asked a question about the proper way to go about creating a class in C++11. I practiced by building a Tree class, and I received some wonderful advice. However, I'm having a little trouble understanding why my code is not working.
In particular, I'm having trouble understanding why my insert method is not working correctly.
template<typename T>
class Tree {
private:
struct Node {
T data;
Node* p_left;
Node* p_right;
};
Node* newNode(T data) { return new Node {data, nullptr, nullptr}; }
Node* root_;
//Other functions, etc... (copy constructor and copy assignment operator)
public:
void insert(T const data) {
Node*& root = root_;
while (root != nullptr) {
root = (data <= root->data ? root->p_left : root->p_right);
}
root = newNode(data);
}
Tree(): root_(nullptr) {}
//Other constructors, functions, etc...
};
If I create a new Tree object, and then populate that object with some data, the object only retains the last piece of inserted data. I know I'm messing up somewhere because of my pointer reference, but I can't figure out where. Any tips in the right direction would be much appreciated.

Two issues... first, on the very first insert into the Tree, you are not updating the root:
if (root == nullptr) { return newNode(data); }
In fact, you're returning the new node, even though the insert function returns void. If you remove that line entirely, the code should work. If root starts out as nullptr, the while loop will be skipped and root will be updated to a new node.
Second issue is that you're using a reference to a Node pointer, which means that you're moving your Tree's root_ every time you create a new node. That's not necessarily a good idea. Personally, I'd write it like this:
void insert(T const data) {
Node** proot = &root_;
while (*proot != nullptr) {
proot = (data <= (*proot)->data ? (*proot)->p_left : (*proot)->p_right);
}
*proot = newNode(data);
}

Related

Deep copy queue recursively C++ (Implemented using a doubly linked list)

I'm building a queue using a doubly linked list. I want my deep copy constructor to recursively deep copy the entire queue however I get a segmentation fault when I do the following:
// Recursive helper method for deep copy constructor
void queue::copyAllNodes(Node* og, Node *cpy) {
if(og == nullptr) back = og;
else {
cpy->previous = og->previous;
cpy->data = og->data;
cpy->next = og->next;
copyAllNodes(og->next, cpy->next);
}
}
my constructor has another helper function that allows me to pass in the original and the copy node i want to copy.
First, understand you're not actually copying anything here. You're just enumerating by recursion and assigning pointers. At best that is a shallow copy; in reality your algorithm is completely broken. There are ways to copy a linked list recursively, including bidirectional linked lists. Whether it is wise to do so is another story.
Unidirectional List
Before getting into the subject of a bidirectional linked list, consider the base case of a unidirectional list. Suppose you have a linked list with nodes like this:
template<class T>
struct Node
{
T data;
Node *next;
Node(T dat, Node *nxt = nullptr)
: data(std::move(dat))
, next(nxt)
{
}
};
Now suppose you want to make a deep copy of this recursively. In its most simple form the algorithm for a recursive copy would be:
Node *copyDeep(const Node *p)
{
Node *res = nullptr;
if (p)
res = new Node(p->data, copyDeep(p->next));
return res;
}
Bidirectional List
Introducing a double-linked node (both next and prev members) makes this a bit more challenging, but not by much. First the node has to be modified to include the new member:
struct Node
{
T data;
Node *next;
Node *prev;
Node(T dat, Node *nxt = nullptr)
: data(std::move(dat))
, next(nxt)
, prev(nullptr)
{
}
};
With that, copyDeep can be modified to remember the added node to be used as an added argument to the recursed call. One way of doing that is:
Node *copyDeep(Node *p, Node *prev = nullptr)
{
if (p)
{
Node *res = new Node(p->data);
res->prev = prev
res->next = copyDeep(p->next, res);
return res;
}
return nullptr;
}
In both cases it is easier, faster, and less error prone (including stack overflows) to do this iteratively. In answer to your question, yes you can recursively copy a linked list. Whether it's a good idea to do so I leave to you.

Establishing an Entry Class for a Linked List

I have a new project where I am creating a class for an entry in a doubly linked list. I am utilizing object-oriented style, which I have limited experience with. The constructors and functions were defined in a separate file.
Header File:
#ifndef LISTENTRY_H_JDP
#define LISTENTRY_H_JDP
#include "DATAClass.h"
#include <iostream>
using namespace std;
typedef DATAClass l;
typedef class LISTEntry *listptr;
class LISTEntry
{
DATAClass data;
listptr prev;
listptr next;
public:
LISTEntry();
LISTEntry(DATAClass l);
LISTEntry(LISTEntry &le);
~LISTEntry();
LISTEntry getNext();
void setNext();
LISTEntry getPrev();
void setPrev();
DATAClass getData();
void setData(DATAClass d);
};
#endif // LISTENTRY_H_INCLUDED
Implementation File:
#include "LISTEntry.h"
LISTEntry::LISTEntry()
{
data = data;
prev = NULL;
next = NULL;
}
LISTEntry::LISTEntry(DATAClass l) //take an item of type l and convert it into a LISTEntry
{
data = l;
prev = NULL;
next = NULL;
}
LISTEntry::LISTEntry(LISTEntry &le)
{
data = le.getData();
prev = le.getPrev();
next = le.getNext();
}
LISTEntry::~LISTEntry()
{
}
LISTEntry LISTEntry::getNext()
{
return *next;
}
void LISTEntry::setNext()
{
next = new LISTEntry;
}
LISTEntry LISTEntry::getPrev()
{
return *prev;
}
void LISTEntry::setPrev()
{
prev = new LISTEntry;
}
DATAClass LISTEntry::getData()
{
return data;
}
void LISTEntry::setData(DATAClass d)
{
data = d;
}
The issue is my copy constructor, LISTEntry(LISTEntry &le). So far, I receive the error:
cannot convert 'LISTEntry' to 'listptr {aka LISTEntry*}'
I am also unsure about the get and set functions. I want them to link to new entries of the same type in the list. I guess I am having trouble with the implementation of pointers in the constructor. Can anyone help out?
You could solve the problem by removing the copy constructor, but this hides the problem that caused the error.
Unless l is poorly written (violates the Rules of Three or Five), there is no need for a copy constructor or destructor in LISTEntry. LISTEntry has no special resources of its own and should be able to observe the Rule of Zero. If l is broken, fix l, do not inflict its flaws on other classes.
But this is not what you want to do for a couple reasons.
The underlying problem causing the error message is prev = le.getPrev(); is attempting to assign a copy of the source's LISTEntry's previous node to the new LISTEntry's pointer to the previous node.
prev needs the address of a LISTEntry, not a LISTEntry.
In a linked list LISTEntry LISTEntry::getNext()and LISTEntry LISTEntry::getPrev() should almost certainly not return a copy of the node pointed at. You want to return the pointer. If you do not, you will find that iterating through the linked list is an adventure. You'll be operating on, possibly modifying, copies of nodes rather than the originals. Chaos ensues.
Change them to LISTEntry * LISTEntry::getNext() and remove the dereference in the return statement.
This solves the error, and a few more you hadn't found yet, but leaves you with a different problem, and the same one you'd have if you removed the copy constructor. You now have two LISTEntry with the same prev and next. This can make for an unstable list. With the copy you can blow the crap out of the original's list. Not cool. Be careful. You are actually better off NOT copying the the links and making the copy constructor:
LISTEntry::LISTEntry(const LISTEntry &le) // make everything const until proven otherwise
{
data = le.data; // this is a member function so it can access private variables
// no need for the accessor function
prev = nullptr;
next = nullptr;
}
You also need/want an assignment operator
LISTEntry & operator=(const LISTEntry &le)
{
if (this != &le)
{
data = le.data;
prev = nullptr;
next = nullptr;
}
}
You should also discuss
void LISTEntry::setNext()
{
next = new LISTEntry;
}
with your rubber duck. Ducky wants to know do you plan to link an existing node if you always create a new one? This will make it really hard to insert, remove and sort.

Unique Pointer attempting to reference a deleted function

Hello I am trying to use pointers and learning the basics on unique pointers in C++. Below is my code I have commented the line of code in main function. to debug the problem However, I am unable to do so. What am I missing ? Is my move() in the insertNode() incorrect ? The error I get is below the code :
#include<memory>
#include<iostream>
struct node{
int data;
std::unique_ptr<node> next;
};
void print(std::unique_ptr<node>head){
while (head)
std::cout << head->data<<std::endl;
}
std::unique_ptr<node> insertNode(std::unique_ptr<node>head, int value){
node newNode;
newNode.data = value;
//head is empty
if (!head){
return std::make_unique<node>(newNode);
}
else{
//head points to an existing list
newNode.next = move(head->next);
return std::make_unique<node>(newNode);
}
}
auto main() -> int
{
//std::unique_ptr<node>head;
//for (int i = 1; i < 10; i++){
// //head = insertNode(head, i);
//}
}
ERROR
std::unique_ptr>::unique_ptr(const std::unique_ptr<_Ty,std::default_delete<_Ty>> &)' : attempting to reference a deleted function
Aside from other small problems, the main issue is this line:
return std::make_unique<node>(newNode);
You are trying to construct a unique pointer to a new node, passing newNode to the copy constructor of node. However, the copy constructor of node is deleted, since node contains a non-copyable type (i.e. std::unique_ptr<node>).
You should pass a std::move(newNode) instead, but this is problematic since you create the node on the stack and it will be destroyed at the exit from the function.
Using a std::unique_ptr here is a bad idea in my opinion, since, for example, to print the list (or insert into the list), you need to std::move the head (so you lose it) and so on. I think you're much better off with a std::shared_ptr.
I was having the same problem and indeed using a shared_ptr works.
Using the smart pointer as an argument in the function copies the pointer (not the data it points to), and this causes the unique_ptr to reset and delete the data it was previously pointing at- hence we get that "attempting to reference a deleted function" error. If you use a shared_ptr this will simply increment the reference count and de-increment it once you are out of the scope of that function.
The comments in the answers above suggest that using a shared_ptr is baseless. These answers were written before the C++17 standard and it is my understanding that we should be using the most updated versions of the language, hence the shared_ptr is appropriate here.
I don't know why we have to expose node type to user in any case. Whole thingamajig of C++ is to write more code in order to write less code later, as one of my tutors said.
We would like to encapsulate everything and leave no head or tail (pun intended) of node to user. Very simplistic interface would look like:
struct list
{
private:
struct node {
int data;
std::unique_ptr<node> next;
node(int data) : data{data}, next{nullptr} {}
};
std::unique_ptr<node> head;
public:
list() : head{nullptr} {};
void push(int data);
int pop();
~list(); // do we need this?
};
The implementation does something what Ben Voigt mentioned:
void list::push(int data)
{
auto temp{std::make_unique<node>(data)};
if(head)
{
temp->next = std::move(head);
head = std::move(temp);
} else
{
head = std::move(temp);
}
}
int list::pop()
{
if(head == nullptr) {
return 0; /* Return some default. */
/* Or do unthinkable things to user. Throw things at him or throw exception. */
}
auto temp = std::move(head);
head = std::move(temp->next);
return temp->data;
}
We actually need a destructor which would NOT be recursive if list will be really large. Our stack may explode because node's destructor would call unique_ptr's destructor then would call managed node's destructor, which would call unique_ptr's destructor... ad nauseatum.
void list::clear() { while(head) head = std::move(head->next); }
list::~list() { clear(); }
After that default destructor would ping unique_ptr destructor only once for head, no recursive iterations.
If we want to iterate through list without popping node, we'd use get() within some method designed to address that task.
Node *head = list.head.get();
/* ... */
head = head->next.get();
get() return raw pointer without breaking management.
How about this example, in addition to the sample code, he also mentioned some principles:
when you need to "assign" -- use std::move and when you need to just traverse, use get()

Removing last element in LinkedList (C++)

I am writing a function to delete the last node in a linked list. This is what I have, and other code I've found online searching for a solution is very similar (I have found several), but when I execute it it creates some sort of infinite loop when deleting the last element of a linked list (it deletes other elements just fine though).
Here is the code I imagine is causing a problem:
void delete_final(Node* head){
if(head == NULL) {
return; }
if(head->next == NULL) {
delete head;
head = NULL;
return;
}
//other code
}
I imagine it's an issue with the memory (particularly after the delete head; statement), but I'm really stuck and would appreciate any help or an explanation for why this doesn't work (I possibly don't have a very good understanding of pointers and memory in C++, I'm just starting with it)
Here is my Node code for reference:
struct Node {
int key;
Node* next;
};
Thanks for any help!
Original code:
void delete_final(Node* head){
if(head == NULL) {
return; }
if(head->next == NULL) {
delete head;
head = NULL;
return;
}
//other code
}
The "other code" is not specified, but if the list has exactly one node then the above code will
delete that first node, and
update the local pointer head, which doesn't update the actual argument since it was passed by value.
As a result the calling code will be left with a dangling pointer in this case, a pointer pointing to a destroyed object, or to where such an object once was. Any use of such a pointer is Undefined Behavior. It might appear to work, or crash, or just silently cause dirty words tattoo to appear on your forehead – anything…
One fix is to pass the first-pointer by reference:
void delete_final(Node*& head){
if(head == nullptr) {
return; }
if(head->next == nullptr) {
delete head;
head = nullptr;
return;
}
//other code
}
A nice helper function for dealing with linked lists, is unlink:
auto unlink( Node*& p )
-> Node*
{
Node* const result = p;
p = p->next;
return result;
}
The implementation is perhaps a bit subtle, but all you need to remember to use it is that it updates the pointer you pass as argument, which should be either a first-node pointer or a next pointer in the list, and returns a pointer to the unlinked node.
So e.g. you can do
delete unlink( p_first );

C++ Linked List Segmentation Fault

I'm writing a program as an assignment for school and I though I had worked out all the bugs until I decided to call my copy constructor. The program is interactive (CLI) which basically has two moduals: a .h and .cpp file for the LList class and a .h and .cpp file for the structure of the program and also a 3rd cpp file just for main(). It is suppose to be a program for a hydropower engineering company (fake company) in which the LList nodes hold data for their annual water flow in a river(year and flow). Here is some insight on the class:
//list.h
struct ListItem {
int year;
double flow;
};
struct Node {
ListItem item;
Node *next;
};
class FlowList {
public:
FlowList();
// PROMISES: Creates empty list
FlowList(const FlowList& source);
// REQUIRES: source refers to an existing List object
// PROMISES: create the copy of source
~FlowList();
// PROMISES: Destroys an existing list.
FlowList& operator =(const FlowList& rhs);
// REQUIRES: rhs refers to an existing FlowList object
// PROMISES: the left hand side object becomes the copy of rhs
//....Other member functions
private:
// always points to the first node in the list.
Node *headM;
// Initially is set to NULL, but it may point to any node.
Node *cursorM;
//For node manipulation within interactive CLI
void copy(const FlowList& source);
void destroy();
I belive the memory leak or collision is taking place somewhere within the copy function but cant pin point where.
//list.cpp
FlowList::FlowList() : headM(0), cursorM(0) {}
FlowList::FlowList(const FlowList& source)
{
copy(source);
}
FlowList::~FlowList()
{
destroy();
}
FlowList& FlowList::operator =(const FlowList& rhs)
{
if (this != &rhs)
{
destroy();
copy(rhs);
}
return (*this);
}
//...more function definitions
void FlowList::copy(const FlowList& source)
{
if (source.headM == NULL)
{
headM = NULL;
cursorM = NULL;
return;
}
Node* new_node = new Node;
assert(new_node);
new_node->item.year = source.headM->item.year;
new_node->item.flow = source.headM->item.flow;
new_node->next = NULL;
headM = new_node;
Node* thisptr = new_node;
for(Node* srcptr = source.headM->next; srcptr != NULL; srcptr = srcptr->next)
{
new_node = new Node;
assert(new_node);
new_node->item.year = srcptr->item.year;
new_node->item.flow = srcptr->item.flow;
new_node->next = NULL;
thisptr->next = new_node;
thisptr = thisptr->next;
}
}
void FlowList::destroy()
{
Node* ptr = headM;
Node* post = headM->next;
while (ptr != NULL)
{
delete ptr;
ptr = post;
if (post)
post = ptr->next;
}
headM = NULL;
}
The program works fine if I create a FlowList object, fill it with data from a .dat file; i can then manipulate the data within the program (display, perform calculations, add to the list, remove from list and save data to file) but program crashes (segmentation fault) if I create another FlowList object (within main.cpp).
Any help would be really appreciated.
The initial thing I spot is that it looks like your destroy() function will always segmentation fault if the list is empty:
void FlowList::destroy()
{
Node* ptr = headM;
Node* post = headM->next;
//...
}
If the list is empty, headM is NULL and then you're trying to do headM->next which will always produce a segmentation fault in that case.
I think you might also have a memory leak in your copy constructor if you pass in an empty list. If you look at this code:
void FlowList::copy(const FlowList& source)
{
if (source.headM == NULL)
{
headM = NULL;
cursorM = NULL;
return;
}
//...
}
What if the current list contained 20 items and source is an empty list? You set the current list's headM and cursorM to NULL, but you never call delete on any of the nodes in the current list that you originally used new to create. You probably want to work your destroy() function somewhere into your copy constructor too (you did it for the operator= function).
The last thing I noticed is that you don't initialize cursorM for a non-empty list in your copy() function (#Akusete pointed that out as well). I think I'd recommend that at the beginning of your copy constructor, just initialize cursorM and headM to NULL just to cover your bases.
It looks like you're really close, I think you just really need to think through the boundary case of dealing with empty lists (both on the LHS and RHS) and you'll probably find most of these bugs. Good luck!