shared pointer swap method identity change - c++

Background:
I donot know what caused me to experiment with this, but I am trying to test a containerised linked list which internally uses smartpointers.
Here is the repl link:
https://repl.it/#BasavarajNagar/GoodnaturedGlisteningSection-1
#include <memory>
using namespace std;
template<typename T>
class linkedlist {
private:
class node {
public:
T data;
shared_ptr<node> next;
node(T data) {
this->data = data;
}
};
// non const unique ptr
shared_ptr<node> head;
public:
void add(T data) {
cout<< "adding " << endl;// + data << endl;
if (head == nullptr) {
head = shared_ptr<node>(new node(data));
return;
}
shared_ptr<node> cursor;
cursor.swap(head);
// this works well
while (cursor != nullptr) {
cout << cursor->data << endl;
cursor = cursor->next;
}
cout << data << endl;
// this is the problematic assignment
cursor = shared_ptr<node>(new node(data));
}
void trace() {
shared_ptr<node> cursor;
cursor.swap(head);
cout << "trace" << endl;
while (cursor != nullptr) {
cout << cursor->data << endl;
cursor = cursor->next;
}
}
};
int main() {
std::cout << "Hello World!\n";
linkedlist<int> ll;
ll.add(22);
ll.add(45);
ll.add(63);
ll.trace();
}
The trace method always points to last element, the head is lost after swap during add method.
Note:
I know this is not production quality code, but to understand internals/quirks of smartpointers. So, pls avoid code quality comments.

You badly misunderstand shared pointers. https://en.cppreference.com/w/cpp/memory/shared_ptr/shared_ptr
The trace info you need most is node::node and node::~node. Tracking when actual nodes get created and destroyed will help you. You also must understand scope.
A quick critique of your "add" function (fixes left to you, beyond the scope of the question and detrimental for me to post too much):
It creates a single item list correctly. However, if you try to add again, you move head to a temporary object, setting head to nullptr. You then loop cursor through the list, destructing any nodes that might have existed, until there are no more. The fact that you have just assigned nullptr to cursor is not a problem, because you immediately toss whatever value it might have had as you then create a new list with a single item, held by cursor instead of head. You then go out of scope, destructing cursor, and therefore also destructing that new item that you just added to cursor.
But the biggest problem is your trace function, which you are using to understand your list, but it's not doing anything remotely like what you want. This is the biggest problem, because you think you understand what is going on based on bad information. If trace lies to you, then you can't get anywhere using it to understand add.
Here is a trace function which will correctly print the current contents of your list, without destroying it:
void trace() {
cout << "trace: ";
shared_ptr<node> cursor = head; // SHARE, do not lose head, read the docs
while (cursor.get()) {
cout << cursor->data << ", ";
cursor = cursor->next;
}
cout << endl;
}
I would suggest calling the trace function twice in a row. If it's not destroying the list as it prints it, then the second call should have the same output as the first. To fix add, you need to simplify it. Just follow what you should do on a regular node pointer. Your big problem was using "swap" to place your actual data under the sole control of a temporary object, which will quickly DO ITS JOB, which is destroy all your data.

Related

Why I can't implement linked list on stack memory instead of heap?

struct node{
int data;
node* next;
}
int main(){
Node *head; head->data = 999;
Node *new_node; new_node->data = 1; head->next = new_node;
cout << (head->next)->data;
}
This code doesn't work. If I use allocation, it works. I try searching on Google but no one ask about this.
My code based on my understand about linked list. So feel free to roast me.
When you create
Node* You are creating a node pointer not a node. This is just a variable that will hold a memory address, and that memory address will hold a node. Since you aren't allocating any memory there isn't actually a node to use when you use the -> operator. Basically its not going to work in your code because you're trying to dereference an uninitialized pointer and then modify memory that you haven't allocated yet.
As far as the overall question of why you can't make a linked list with static memory, that is because of scope rules and automatic memory management on the stack. The idea of the linked list is that you are linking nodes together based on their memory address. If you create each node on the stack those nodes will be deleted after they go out of scope, so even if you keep pointers to their memory address it is not safe to assume they wont be overwritten by something else. This of course would only be the case if you don't keep the entire thing within the same scope and would likely not be what you want.
As #drescherjm suggested in the comments, you could make a static array of Node and index into that. The advantage of that is that the code only differs in the allocation function. For dynamic allocation, you would make a node with new Node and with a static array you would use &heap[++size];
Note that if the array is global it isn't actually on the stack - it needs to be a local variable to be on the stack.
#include <iostream>
struct Node
{
long data;
Node *next;
};
Node heap[1000];
int capacity = 1000;
int size = 0;
Node *alloc()
{
// For dynamic allocation you would:
// return new Node;
// For stack allocation you would:
// return &heap[++size];
if (size >= capacity)
throw std::bad_alloc();
return &heap[size++];
}
int main()
{
Node *head = alloc();
head->data = 999;
std::cout << "head: " << head << " (value: " << head->data << ")\n";
Node *new_node = alloc();
new_node->data = 1;
head->next = new_node;
std::cout << "node: " << new_node << " (value: " << (head->next)->data << ")\n";
std::cout << "heap: " << heap << " (used/capacity: " << size << "/" << capacity << ")\n";
return 0;
}
Try it at https://onlinegdb.com/r1g-w4LKw

Trying to write my own linked list impementation in c++, code segfaults after hitting 3 elements in the list

I've been trying to write my own implementation of linked list, but the code segfaults when I try to access an the third element or anything after it. Adding elements doesn't segfault, but accessing does. I can't find the pointer error in my get() function.
Each node in the list stores data (of Template t) and a pointer leading to the next node. I have two functions for everything- one for the first element, and one for any subsequent elements. The get() function for the subsequent elements always segfaults. I have some debug messages in the function that spit out results I can't explain. For example, if I run a get() request for the second element, an then the third, the code doesn't segfault, but it does return clearly incorrect results. Debug messages I placed indicate the segfault occurs when the second element calls the function to check the third element, if it occurs at all. Try the code with and without the line cout << newList.get(2) << endl; and you'll get very different results.
One possible cause is the pointer storage- I have the get() function output the pointer of each element (except the first) as it cycles through, and compare them to the pointers outputted by the add() function, and and pointers for element 0 and 1 match, but 2 and beyond do not match, and I can't seem to figure out why that would be.
#include <iostream>
using namespace std;
template <class T> class myLinkedList{
T data;
myLinkedList<T> *next = NULL;
public:
myLinkedList(T input){
data = input;
}
void add(T input){
if(next == NULL){
myLinkedList<T> newItem(input);
next = &newItem;
cout << "adding to list, data is " << input << ", pointer is " << next << endl;
}else{
myLinkedList<T> nextEntry = *next;
nextEntry.add(input);
}
}
T getData(){
return data;
}
//the start of the get function, only used by the first entry in the list
T get(int entry){
int currentPosition = 0;
if(entry == currentPosition){
return getData();
}else{
//defrefrence the pointer anc check the next entry
myLinkedList<T> nextEntry = *next;
return nextEntry.get(entry, ++currentPosition);
}
}
private:
//this vesion is the hidden, private vesion only used by nodes other than the first one
//used to keep track of position in the list
T get(int entry, int currentPosition){
//cout << currentPosition << endl;
if(entry == currentPosition){
return data;
}else{
//derefrence the pointer and check the next entry
cout << next << endl;
myLinkedList<T> nextEntry = *next;
currentPosition++;
T output = nextEntry.get(entry, currentPosition);
return output;
}
}
};
int main(){
myLinkedList<int> newList(3);
newList.add(4);
newList.add(5);
newList.add(7);
newList.add(9);
cout << newList.get(2) << endl;
cout << newList.get(3) << endl;
return 0;
}
Results are clearly erroneous- program should spit oout two macthing sets of pointers, as well as the numbers 5 and 7 ( the list elements)
One of your main problems is here:
if(next == NULL){
myLinkedList<T> newItem(input); // <<<<<<<<<<<<<
next = &newItem;
cout << "adding to list, data is " << input << ", pointer is " << next << endl;
}
you allocate an item on stack inside the if scope. Then you make next to point to this item. But... lifetime of the item is bounded by this scope. As son as you exit the scope, this item does not exist any longer. You need to allocate it dynamically by 'new' or other methods.
I had a breakthrough! Following Serge's solution was helpful, but one more change was needed- rather than create a function reference in the else block of my add function,
eg
myLinkedList<T> nextEntry = *next;
nextEntry.add(input)
i needed to use the pointer directly, as in
next->add(input)
I didn't know my pointer/object syntax

linked list not working properly, head updated instead

I have a problem with the following linked list code, but i'm not sure what it is. Could someone point me in the right direction? I'm using this code in some larger code where i update records, but it never reaches the "creating new record" section. It is as if the main code is updating the head pointer instead thus always resulting in a favourable comparison.
Thanks in advance. I've been racking my brains out trying to figure out what the problem is.
struct l_list *find_name(const char *name)
{
struct l_list *tmp=0;
if(records==0) { // First record
head=new l_list;
head->name=name;
head->next=0;
tail=head;
records++;
return head;
}
else {
tmp=head;
while(tmp!=0)
{
if(!std::strcmp(tmp->name,name))
{
cout << "Returning existing record with value: " << tmp->number << " name:" << tmp->name << endl;
return tmp;
}
tmp=tmp->next;
}
// No first and no existing records
cout << "Creating new record" << endl;
tail->next=new l_list;
tail=tail->next;
tail->name=name;
tail->next=0;
records++;
return tail;
}
I'm calling this from main with:
struct records *tmp=find_name("Max");
then :
tmp=find_name("Eva");
Once i get the struct i update it like so:
tmp->number=1;
Or even updating the name:
tmp->name="Peter";
So by passing a string to the function it will either create a new record and return it or give an existing record and return that. Problems might not be apparent in the output, but when you put it in a for(;;) loop in main it will
mess up.
The struct is as follows:
struct records {
const char *name;
struct records *next;
}
The relevant program code is:
struct record {
const char *name;
struct record *next;
};
struct record *head;
struct record *tail;
struct record *find_name(const char *name)
{
struct record *tmp=0;
if(record_count==0) { // First record
cout << "Creating first record" << endl;
head=new record;
head->name=name;
head->next=0;
tail=head;
record_count++;
return head;
} else {
tmp=head;
while(tmp!=0) {
if(!std::strcmp(tmp->name,name)) {
cout << "Returning existing record with value: " << "name: " << name << "tmp->name: " << tmp->name << endl;
return tmp;}
tmp=tmp->next;
}
// No first and no existing records
cout << "Creating new record" << endl;
tail->next=new record;
tail=tail->next;
tail->name=name;
tail->next=0;
record_count++;
return tail;
}
}
int main(int argc, const char *argv[])
{
struct record *tmp=0;
if(something is true) {
//Return or create a new user
tmp=find_name("Peter");
} else {
tmp=find_name("Unknown"); // Hold 1 unknown person in database
}
}
I know it's not compilable as-is but i had to extract it from a larger part.
Since you have not told us what the records structure is, there is no way for anyone to give a correct answer. You have made it even more impossible by not giving an example of code that will cause your function to behave incorrectly.
If the name element is a char * pointer then you could easily get this behaviour. For example:
The calling code puts a name into a buffer, and calls find_name.
find_name stores the address of the buffer into the name element of a records object. name therefore points to whatever the buffer happens to contain, now and in the future.
The calling code puts a new name into the same buffer. This automatically means that the name element now points to that new name, since it is pointing to the buffer.
The calling code calls find_name again.
find_name compares the contents of the buffer to the string pointed to by the name element of the first records object. Since the name element contains the address of the buffer passed by the caller (from step 2), this means that it is comparing the buffer to itself. So the result is always "equal".
But it may be that name is not a pointer at all, in which case this entire answer is irrelevant.
First of all do not use the following code formatting
if(record_count==0) { // First record
cout << "Creating first record" << endl;
//...
} else {
tmp=head;
//...
It is difficult to read such a code. It is just a bad style of programming.
The function itself can look the following way
struct l_list * find_name( const char *name )
{
struct l_list *tmp = head;
wjile ( tmp != nullptr && std::strcmp( tmp->name, name ) != 0 ) tmp = tmp->next;
if ( tmp == nullptr )
{
cout << "Creating new record" << endl;
tmp = new l_list;
tmp->name = name;
tmp->next = nullptr;
if ( tail == nullptr )
{
head = tail = tmp;
}
else
{
tail = tail->next = tmp;
}
records++;
}
return tmp;
}
Take into account that the nodes can contain pointers to strings either with the static storage duration as for example string literals or allocated in the heap.

Trouble calling an object's method using a pointer to that object

So I normally wouldn't bother posting about such a simple thing but I can't for the life of me figure out what I'm doing wrong and it's probably something very simple that i'm over looking.
Getting straight to the point, I'm making an object pointer and then using that pointer to call a method of that particular object.
But for some reason whenever I cout the function as follows:
Entry FirstEntry = Entry(J1,H1);
cout << FirstEntry.getItem() << endl;
cout << FirstEntry.getKey() << endl;
Entry *siz = new Entry();
cout << siz->getItem() << endl;
It gives me a blank line being output. Whereas it should be giving me my item value.
The Object itself looks like such:
Entry::Entry()
{
}
Entry::Entry(KeyType & Key, ItemType & newItem)
{
setKey(Key);
setItem(newItem);
}
Entry::~Entry()
{
}
ItemType Entry::getItem() const
{
return item;
}
KeyType Entry::getKey() const
{
return searchKey;
}
void Entry::setItem(const ItemType & newItem)
{
item = newItem;
}
void Entry::setKey(const KeyType & Key)
{
searchKey = Key;
}
I've been staring at this code for awhile and I can't quite figure out what's causing it to output just a blank line.
Thank you very much for your patience,
Okay, thanks to the two posts it's super obvious what I'm doing wrong here, when I created my pointer to my object I was pointing towards the constructor which didn't set any values and thus when I was returning values through my pointer technique it was giving me the accurate values. They were just empty values, hence it was putting out an empty line.
The Fix:
Entry *siz = new Entry(J1,H1); //This line didn't hold parameters to call right constructor
cout << siz->getItem() << endl;

c++ delete entire binary search tree

for some reason my nodes don't seem to be deleting. it looks as though it traverses to the end ok but after the node is "deleted" it still has the data in it. i've also tried
free(bNode) and bNode = NULL instead of delete bNode but they all give the same result.
The cout and display functions were just put in when I was trying to debug. I just don't understand why its not working, i hope i'm not missing something simple.
struct
Book{
char title [50];
char url [75];
Book *left;
Book *right;
};
void deleteAllBooks (Book *bNode){
if(bNode==NULL) return;
if(bNode->left !=NULL){
cout << endl << "deleting left" << endl;
deleteAllBooks(bNode->left);
}
if(bNode->right !=NULL){
cout << endl << "deleting right" << endl;
deleteAllBooks(bNode->right);
}
cout << endl << "deleting node " << bNode->title << endl;
delete bNode;
displayBookTree(bNode);
}
void displayBookTree(Book *bNode){
if(bNode==NULL){
cout << "No books" << endl;
return;
}
if(bNode->left !=NULL){
displayBookTree(bNode->left);
}
if(bNode->right !=NULL){
displayBookTree(bNode->right);
}
cout <<"Title: " << bNode->title << endl;
cout <<"URL: " << bNode->url <<endl;
}
"Use 0. The "NULL" macro is not type-safe; if you feel that you must
use "null", make it a const int instead of the C-style "#define". Also
see "The C++ Programming Language" for Stroustrup's argument against
the usage of "NULL"."
I would try to change:
if (bNode==NULL) { ... }
with
if (!bNode) { ... }
And
if (bNode->left !=NULL) { ... }
if (bNode->right !=NULL) { ... }
with
if (bNode->left) { ... }
if (bNode->right) { ... }
And then take a look to this answer on how correctly delete a Struct!
Easiest solution:
struct Book{
std::string title;
std::string url;
std::unique_ptr<Book> left;
std::unique_ptr<Book> right;
};
void deleteAllBooks (std::unique_ptr<Book> bNode)
{
// No code necessary. Literally. But usually you wouldn't even
// bother with this function, the default Book::~Book is fine.
}
Your solution is correct, but your observations are wrong. When you delete an object, the destructor will be executed for that object. In your case, this destructor has no visible side effect, because all your data members are plain old data types that do not have a destructor on there own. Using an object after it was deleted, invokes undefined behavior and your observation is one possible incarnation of "undefined behavior".
Your test for != 0 before calling deleteAllBooks() is redundant:
void deleteAllBooks ( Book *node )
{
if( node )
{
deleteAllBooks( node->left );
deleteAllBooks( node->right );
delete node;
}
}
does the same, but might be easier to understand.
And don't mix free/alloc with new/delete. If you've allocated an object with new, you have to return it with delete. Otherwise, you will get undefined behavior.