I just read up about smart pointers so i wanted to do a real demo example, hence i created the DLL code below, the problem is the nodes are placed properly and all, but nodes memory are not getting freed, not sure what i am doing wrong.
as far as my understanding , when the scope runs out, the nodes must be deleted automatically. please correct me if i am wrong.
Original Code:
#include <iostream>
#include <memory>
using namespace std;
template <typename T>
class DLL {
class Node {
public:
T key;
std::shared_ptr<Node> next;
std::shared_ptr<Node> prev;
Node():key(),next(),prev(){}
Node(T data):key(data),next(),prev(){}
~Node(){
cout << "node deleted \n";
}
};
std::shared_ptr<Node> m_head;
std::shared_ptr<Node> m_tail;
std::size_t length;
public:
DLL() : m_head() ,m_tail() , length(0){
}
virtual ~DLL(){
}
void add_back(T data){
std::shared_ptr< Node > node = std::make_shared<Node>(data);
if(!m_tail){
m_tail = std::move(node);
m_head = m_tail;
}
else{
m_tail->next = std::move(node);
m_tail->next->prev = m_tail;
m_tail = m_tail->next;
}
length++;
}
void add_front(T data){
std::shared_ptr< Node > node = std::make_shared<Node>(data);
if(!m_head){
m_head = std::move(node);
m_tail = m_head;
}
else{
m_head->prev = std::move(node);
m_head->prev->next = m_head;
m_head = m_head->prev;
}
length++;
}
void printNodes(void){
for(std::shared_ptr< Node > temp = m_head; temp ; temp = temp->next) {
cout << temp->key << '\n';
}
}
void addAtPosition(T data , std::size_t pos){
if(pos < 0 || pos >= length) {
throw("Invalid position");
}
if(pos == 0){
add_front(data);
}
else if(pos == length - 1){
add_back(data);
}
else{
std::shared_ptr< Node > temp = m_head;
for(; temp && pos ; temp = temp->next) {
pos--;
}
std::shared_ptr< Node > node = std::make_shared<Node>(data);
std::shared_ptr< Node > prev = temp->prev;
temp->prev = std::move(node);
temp->prev->next = temp;
temp->prev->prev = prev;
prev->next = temp->prev;
length++;
}
}
};
int main(int argc , char** argv){
std::unique_ptr<DLL<int>> m_list = std::make_unique<DLL<int>>();
m_list->add_front(3);
m_list->add_front(2);
m_list->add_front(1);
m_list->add_back(4);
m_list->add_back(5);
m_list->add_back(6);
m_list->addAtPosition(7,0);
m_list->addAtPosition(7,4);
m_list->addAtPosition(7,7);
m_list->printNodes();
return 0;
}
Modified Code:
#include <iostream>
#include <memory>
using namespace std;
template <typename T>
class DLL {
class Node {
public:
T key;
std::shared_ptr<Node> next;
std::weak_ptr<Node> prev;
Node():key(),next(),prev() {}
Node(T data):key(data),next(),prev() {}
~Node(){cout << "deleted \n";}
};
std::shared_ptr<Node> m_head;
std::weak_ptr<Node> m_tail;
std::size_t length;
public:
DLL():m_head(),m_tail(),length(0){}
void addFront(T data){
std::shared_ptr< Node > node = std::make_shared<Node>(data);
if(length == 0){
m_head = std::move(node);
m_tail = m_head;
}
else{
node->next = m_head;
m_head->prev = node;
m_head = std::move(node);
}
length++;
}
void addBack(T data){
std::shared_ptr< Node > node = std::make_shared<Node>(data);
if(length == 0){
m_head = std::move(node);
m_tail = m_head;
}
else{
node->prev = m_tail.lock();
node->prev.lock()->next = std::move(node);
m_tail = m_tail.lock()->next;
}
length++;
}
void addAtPosition(T data , std::size_t pos){
if(pos == 0){
addFront(data);
}
else if(pos == length){
addBack(data);
}
else if(pos < 0 || pos >= length) {
throw("Invalid position");
}
else{
std::shared_ptr< Node > node = std::make_shared<Node>(data);
std::weak_ptr<Node> temp = m_head;
for(int cnt = 0; cnt < pos ; cnt++){
temp = temp.lock()->next;
}
node->next = temp.lock();
node->prev = node->next->prev;
node->prev = std::move(node);
length++;
}
}
void printNodes(void){
std::weak_ptr<Node> wp = m_head;
for(int i = 0; i < length; i++) {
auto& sp = *(wp.lock());
cout << sp.key;
wp = sp.next;
}
}
};
int main(){
std::unique_ptr<DLL<int>> m_list = std::make_unique<DLL<int>>();
for(int i = 0; i < 10 ; i++)
{
try{
m_list->addAtPosition(i,i);
}
catch(const char* mess){
cout << i <<' '<<mess << '\n';
}
}
m_list->printNodes();
return 0;
}
PS: Based on the input i have edited my code and its now working, but still i feel my methods are doing too much work and there is scope of optimization. can someone help me in optimizing my code using smart pointers. Also i am not trying to implement DLL, i just wrote enough code to get a hands-on feel using the new smart pointers.
You have circular references. You must resolve them using std::weak_ptr to manage the prev pointer.
Having a circular reference means the reference counters in the shared_ptr instances won't ever reach zero. Therefore the objects they point to will never be deleted.
Related
#include <iostream>
using namespace std;
template <typename Object>
struct Node
{
Object data;
Node* next;
Node(const Object &d = Object(), Node *n = (Object)NULL) : data(d), next(n) {}
};
template <typename Object>
class singleList
{
public:
singleList() { init(); }
~singleList() { eraseList(head); }
singleList(const singleList &rhs)
{
eraseList(head);
init();
*this = rhs;
print();
contains(head);
}
void init()
{
theSize = 0;
head = new Node<Object>;
head->next = (Object)NULL;
}
void eraseList(Node<Object> *h)
{
Node<Object> *ptr = h;
Node<Object> *nextPtr;
while (ptr != (Object)NULL)
{
nextPtr = ptr->next;
delete ptr;
ptr = nextPtr;
}
}
int size()
{
return theSize;
}
void print()
{
int i;
Node<Object> *current = head;
for(i=0; i < theSize; ++i){
cout << current->data << " ";
current = current->next;
}
}
bool contains(int x)
{
Node<Object> *current = head;
for (int i = 0; i < theSize; ++i){
if (current->data == x){
return true;
}
current = current -> next;
}
return false;
}
bool add(Object x){
if(!contains(x)){
Node<Object> *new_node = new Node<Object>(x);
new_node->data = x;
new_node->next = head;
head = new_node;
//head->next = new_node;
theSize++;
return true;
}
return false;
}
bool remove(int x)
{
if(contains(x)){
Node<Object> *temp = head;
Node<Object> *prev = NULL;
if(temp != NULL && temp ->data == x){
head = temp->next;
delete temp;
return 0;
}else{
while(temp != NULL && temp->data != x){
prev = temp;
temp = temp->next;
}
if(temp ==NULL){
return 0;
}
prev->next = temp->next;
delete temp;
}
return true;
//theSize--;
}
return false;
}
private:
Node<Object> *head;
int theSize;
};
int main()
{
singleList<int> *lst = new singleList<int>();
lst->add(10);
lst->add(12);
lst->add(15);
lst->add(6);
lst->add(3);
lst->add(8);
lst->add(3);
lst->add(18);
lst->add(5);
lst->add(15);
cout << "The original linked list: ";
lst->print();
cout << endl;
lst->remove(6);
lst->remove(15);
cout << "The updated linked list: ";
lst->print();
cout << endl;
cout << "The number of node in the list: " << lst->size() << endl;
return 0;
}
so the output is supposed to be the following:
The original linked list: 5 18 8 3 6 15 12 10
The update linked list: 5 18 8 3 12 10
The number of node in the list: 6
my output gives the original linked list part but then gives a segmentation fault (core dumped) error. I am not sure where my code is wrong but i think it is in my remove().
Some help will definitely be appreciated.
on line 109 i needed to decrement size.
bool remove(int x)
{
if(contains(x)){
Node<Object> *temp = head;
Node<Object> *prev = NULL;
if(temp != NULL && temp ->data == x){
head = temp->next;
delete temp;
return 0;
}else{
while(temp != NULL && temp->data != x){
prev = temp;
temp = temp->next;
}
if(temp ==NULL){
return 0;
}
prev->next = temp->next;
delete temp;
theSize--;
}
return true;
//theSize--;
}
return false;
}
Answer after second update:
had to fix the remove()
#include <iostream>
using namespace std;
template <typename Object>
struct Node
{
Object data;
Node* next;
Node(const Object &d = Object(), Node *n = (Object)NULL) : data(d), next(n) {}
};
template <typename Object>
class singleList
{
public:
singleList() { init(); }
~singleList() { eraseList(head); }
singleList(const singleList &rhs)
{
eraseList(head);
init();
*this = rhs;
print();
contains(head);
}
void init()
{
theSize = 0;
head = new Node<Object>;
head->next = (Object)NULL;
}
void eraseList(Node<Object> *h)
{
Node<Object> *ptr = h;
Node<Object> *nextPtr;
while (ptr != (Object)NULL)
{
nextPtr = ptr->next;
delete ptr;
ptr = nextPtr;
}
}
int size()
{
return theSize;
}
void print()
{
int i;
Node<Object> *current = head;
for(i=0; i < theSize; ++i){
cout << current->data << " ";
current = current->next;
}
}
bool contains(int x)
{
Node<Object> *current = head;
for (int i = 0; i < theSize; ++i){
if (current->data == x){
return true;
}
current = current -> next;
}
return false;
}
bool add(Object x){
if(!contains(x)){
Node<Object> *new_node = new Node<Object>(x);
new_node->data = x;
new_node->next = head;
head = new_node;
//head->next = new_node;
theSize++;
return true;
}
return false;
}
bool remove(int x){
Node<Object> *pCur = head;
Node<Object> *pPrev = pCur;
while (pCur && pCur->data != x) {
pPrev = pCur;
pCur = pCur->next;
}
if (pCur==nullptr) // not found
return false;
if (pCur == head) { // first element matches
head = pCur->next;
} else {
pPrev->next = pCur->next;
}
// pCur now is excluded from the list
delete pCur;
theSize--;
return true;
}
private:
Node<Object> *head;
int theSize;
};
int main()
{
singleList<int> *lst = new singleList<int>();
lst->add(10);
lst->add(12);
lst->add(15);
lst->add(6);
lst->add(3);
lst->add(8);
lst->add(3);
lst->add(18);
lst->add(5);
lst->add(15);
cout << "The original linked list: ";
lst->print();
cout << endl;
lst->remove(6);
lst->remove(15);
cout << "The updated linked list: ";
lst->print();
cout << endl;
cout << "The number of node in the list: " << lst->size() << endl;
return 0;
}
I have implemented a method to insert a new node before a specific node.
#ifndef FORWARD_SINGLY_LINKED_LIST_H
#define FORWARD_SINGLY_LINKED_LIST_H
#include <cstdlib>
#include <string.h>
#include <iostream>
namespace forward_singly_linked_list {
typedef struct Node {
std::string data;
struct Node *nextPointer;
} Node;
typedef Node *NodeP;
class LinkedList {
private:
int elementsCount;
Node *head;
public:
LinkedList() {
head = NULL;
elementsCount = 0;
}
int get_length() const
{
return elementsCount;
}
// ... ... ...
void add_before(std::string value, std::string before)
{
// empty an area in the memory, and
// save the address of the empty area in 'newNode'
Node *newNode = new Node();
// assign 'value' to the 'data' section of the
// area pointed by 'newNode'
newNode->data = value;
Node * copyOfHead = head;
if (copyOfHead == nullptr)
{
// the list is empty.
// there is no possibility to find 'before'.
// so, return.
return;
}
else
{
bool found = false;
Node * previousNode = nullptr;
while (copyOfHead != nullptr)
{
if (copyOfHead->data == before)
{
found = true;
break;
}
else
{
previousNode = copyOfHead;
copyOfHead = copyOfHead->nextPointer;
}
}
if (!found)
{
return;
}
if (previousNode != nullptr)
{
newNode->nextPointer = previousNode->nextPointer;
previousNode->nextPointer = newNode;
}
else
{
newNode->nextPointer = head;
head = newNode;
}
}
elementsCount++;
}
// ... ... ...
void print() {
Node *copyOfHead = head;
while (copyOfHead != NULL) {
std::cout << copyOfHead->data;
copyOfHead = copyOfHead->nextPointer;
}
std::cout<<"\n\n";
}
public:
static int Test() {
forward_singly_linked_list::LinkedList list;
list.print();
// list.add_at_tail("A");
// list.add_at_tail("B");
// list.add_at_tail("C");
list.print();
list.add_at("-XXX-", 1);
list.print();
return 0;
}
};
}
#endif
Personally, I don't like it because it uses an extra pointer previousNode. I have a feeling that it could be improved.
How can I improve the implementation?
The idea is about link the previous node to input/target node, then link the target/input node to current node.
Here is my code using simple loop index(i) instead of using extra pointer
void Sll::add_at_index( int ind , int value ){
Node *target = new Node( value ) ;
if( ind == 1){
target->next = head ;
head = target ;
}else if( ind == length){
tail->next = target ;
tail = target ;
tail->next = nullptr ;
}else{
Node *curr =head ;
for( int i = 1 ; i < length ; i++ ){
if( i+1 == ind){
Node *Mynext = curr->next ;
curr->next = target ;
target->next = Mynext ;
break ;
}
curr = curr->next ;
}
}
length++ ;
}
Become a two-star programmer:
void add_before(std::string value, std::string_view before) {
auto p = &head;
while (*p && (*p)->data != before)
p = &(*p)->nextPointer;
if (!*p)
return;
*p = new Node {
.data = std::move(value),
.nextPointer = *p,
};
++elementsCount;
}
If I run this in visual studio it tells me there are memory leaks, but I don't see anything wrong with my destructor. What did I do wrong? Is it because the memory leak function is being called before the destructor? Am I not supposed to call the memory leak function at the end?
I already posted this on codereview and they said it work fine, but I hadn't included a destructor then. I added one now but I'm not sure if it's actually working.
#define _CRTDBG_MAP_ALLOC
#include <stdlib.h>
#include <crtdbg.h>
#include <iostream>
using std::cout;
using std::cin;
using std::endl;
struct node {
int key;
struct node *next;
};
class linked_list {
private:
struct node *head;
struct node *tail;
public:
linked_list() {
head = nullptr;
tail = nullptr;
}
void create(int key) {
struct node *temp;
temp = new struct node;
temp->key = key;
temp->next = nullptr;
head = temp;
tail = head;
}
void insert(int key) {
if (key < head->key) {
insert_beginning(key);
}
else if ((head->next == nullptr) || (key > tail->key)) {
insert_end(key);
}
else {
insert_middle(key);
}
}
void insert_beginning(int key) {
if (head->next == nullptr) {
tail = head;
}
struct node *temp;
temp = new struct node;
temp->key = key;
temp->next = head;
head = temp;
}
void insert_end(int key) {
struct node *temp;
temp = new struct node;
temp->key = key;
temp->next = nullptr;
if (head->next == nullptr) {
head->next = temp;
tail = temp;
}
else {
tail->next = temp;
}
tail = temp;
}
void insert_middle(int key) {
struct node *temp;
temp = new struct node;
temp->key = key;
struct node *current = head;
struct node *prev = current;
while (current->key < temp->key) {
prev = current;
current = current->next;
}
prev->next = temp;
temp->next = current;
}
void delete_node(int key) {
if (head == nullptr) {
cout << "List is empty\n";
return;
}
if (head->key == key) {
if (head->next == nullptr) {
delete(head);
head = tail = nullptr;
}
struct node *temp = head;
head = head->next;
delete(temp);
}
else {
struct node *current = head;
struct node *prev = current;
while ((current->key != key) && (current->next != nullptr)) {
prev = current;
current = current->next;
}
if ((current->key != key) && (current->next == nullptr)) {
cout << "Key not found\n";
}
else if ((current->key == key) && (current->next == nullptr)) {
tail = prev;
prev->next = nullptr;
delete(current);
}
else {
prev->next = current->next;
delete(current);
}
}
}
void search_node(int key) {
if (head->key == key || tail->key == key) {
cout << "Node found\n";
return;
}
struct node *current = head;
while ((current->key != key) && (current->next != nullptr)) {
current = current->next;
}
if (current->key == key) {
cout << "Node found\n";
}
else {
cout << "Node not found\n";
}
}
void print_nodes(void) {
struct node *current = head;
while (current != nullptr) {
cout << current->key << '\n';
current = current->next;
}
}
~linked_list() {
struct node *current = head;
struct node *prev = current;
while (current->next != nullptr) {
current = current->next;
delete(prev);
prev = current;
}
delete(prev);
}
};
int main(void) {
linked_list list;
list.create(0);
for (int i = 1; i < 20; ++i) {
list.insert(i);
}
list.search_node(5);
list.search_node(0);
list.search_node(-1);
list.delete_node(19);
list.delete_node(0);
list.print_nodes();
_CrtDumpMemoryLeaks();
}
When you call _CrtDumpMemoryLeaks();, you have not yet destructed your list object.
UPDATE
Adding a set of braces so as to destruct list before doing the memory leak diagnostic.
int main(void) {
{
linked_list list;
list.create(0);
for (int i = 1; i < 20; ++i) {
list.insert(i);
}
list.search_node(5);
list.search_node(0);
list.search_node(-1);
list.delete_node(19);
list.delete_node(0);
list.print_nodes();
}
_CrtDumpMemoryLeaks();
}
The usual way to do a leak check as late as possible with MSVC is to enable the automatic dump functionality, with the following code:
_CrtSetDbgFlag(_CrtSetDbgFlag(_CRTDBG_REPORT_FLAG)|_CRTDBG_LEAK_CHECK_DF);
This will do a leak check after main/WinMain has returned and global object destructors have run. And if you've ever seen an MFC dump report that is how it is enabled.
When I run valgrind, I get one error at method insert for operator new;
I know this probably means that I have to delete node n how I tried so many things to try to delete it but it just gives me even more errors. Please hel.
class key_value_sequences {
public:
struct node{
int key;
vector<int> values;
node* next;
node* prev;
};
key_value_sequences() {
}
~key_value_sequences() {
}
key_value_sequences(const key_value_sequences& A) {
n = A.n;
head = A.head;
tail = A.tail;
v = A.v;
}
key_value_sequences& operator=(const key_value_sequences& A) {
if (this == &A) return *this;
n = A.n;
head = A.head;
tail = A.tail;
v = A.v;
return *this;
}
// YOU SHOULD USE C++ CONTAINERS TO AVOID RAW POINTERS
// IF YOU DECIDE TO USE POINTERS, MAKE SURE THAT YOU MANAGE MEMORY PROPERLY
// IMPLEMENT ME: SHOULD RETURN SIZE OF A SEQUENCE FOR GIVEN KEY
// IF NO SEQUENCE EXISTS FOR A GIVEN KEY RETURN -1
int size(int key) const {
if (find(v.begin(), v.end(), key)!=v.end()) {
node* temp = head;
while(temp != NULL) {
if (temp->key == key) {
return temp->values.size();
}
else temp = temp->next;
}
}
else return -1;
}
// IMPLEMENT ME: SHOULD RETURN POINTER TO A SEQUENCE FOR GIVEN KEY
// IF NO SEQUENCE EXISTS FOR A GIVEN KEY RETURN nullptr
const int* data(int key) const {
if (find(v.begin(), v.end(), key)!=v.end()) {
node* temp = head;
while(temp != NULL) {
if (temp->key == key) {
return temp->values.data();
}
else temp = temp->next;
}
}
else return nullptr;
}
// IMPLEMENT ME: INSERT VALUE INTO A SEQUENCE IDENTIFIED BY GIVEN KEY
void insert(int key, int value) {
if(v.size() == 0) { //empty list
v.push_back(key);
n = new node;
n->prev = NULL;
n->key = key;
n->values.push_back(value);
head = n;
tail = n;
}
else if((find(v.begin(), v.end(), key)!=v.end())) { //if key exists already
node* temp = head;
while(temp != NULL) {
if (temp->key == key) {
temp->values.push_back(value);
break;
}
else temp = temp->next;
}
}
else { //if theres no existing key
v.push_back(key);
n = new node;
n->key = key;
n->values.push_back(value);
n->prev = tail;
tail->next = n;
tail = n;
tail->next = NULL;
}
}
private:
vector<int> v;
node* n;
node* head;
node* tail;
}; // class key_value_sequences
#endif // A3_HPP
In insert method:
if(v.size() == 0) { //empty list
v.push_back(key);
n = new node;
n->prev = NULL;
n->key = key;
n->values.push_back(value);
head = n;
tail = n;
}
You are not setting the head->next to NULL. I suspect that could be the problem while inserting second value.
while(temp != NULL) { // <<<<< Is temp uninitialized?
if (temp->key == key) {
temp->values.push_back(value);
break;
}
else temp = temp->next;
Its pretty dangerous to not initialize the pointer to NULL
It looks like in "SortedInsert", the head is always zero and then the code segfaults anyway... really frustrating. Any idea why the head is always zero even though I set it to something, and then why the code segfaults in general?
Thanks
#include <iostream>
#include <cassert>
#include <string>
#include <stdlib.h>
#include <sstream>
using namespace std;
struct Node {
Node* next = 0;
int data;
~Node(){
if (next != 0){
delete next;
}
}
};
void SortedInsert(Node* head, int value){
if(head == 0){
Node* header = new Node;
header->data = value;
head = header;
return;
}
cout << "TEST" << endl;
Node* temp = head;
while(temp != 0){
if(value > temp->data){
Node* insert = temp->next;
Node* otherTemp = new Node;
otherTemp->data = value;
temp->next= otherTemp;
temp->next->next = insert;
}
temp=temp->next;
}
return;
}
int main() {
srand(32);
Node* sortedList = 0;
for (int i = 0; i < 10; i++){
SortedInsert(sortedList, rand() % 100);
}
Node* temp = sortedList;
for (int i=0; i < 9; i++){
assert(temp->data <= temp->next->data);
temp = temp->next;
}
delete sortedList;
}
SortedInsert has its own copy of the head pointer. When you change head inside the function it doesn't affect the value in main. The solution is to pass head by reference or by passing the address.
void SortedInsert(Node** head, int value) {
//Use *head to refer to the head of the list
}
int main() {
...
Node* sortedList = 0;
SortedInsert(&sortedList, ...);
...
}
Or
void SortedInsert(Node*& head, int value) {
//Use head to refer to the head of the list
}
int main() {
...
Node* sortedList = 0;
SortedInsert(sortedList, ...);
...
}
Try the following
void SortedInsert( Node* &head, int value )
{
if ( head == nullptr || value < head->data )
{
head = new Node { head, value };
}
else
{
Node *current = head;
while ( current->next != nullptr && !( value < current->next->data ) )
{
current = current->next;
}
Node *tmp = new Node { current->next, value };
current->next = tmp;
}
}
As for your funcion implementation then the function deals with a copy of the head. Any changes of the copy do not influence on the argument itself. You should pass the head by reference or return the head from the function.