Skip to content

Instantly share code, notes, and snippets.

@mshafae
Created April 26, 2024 20:38
Show Gist options
  • Save mshafae/2a5eaf94a0f7f81f725dc8e29872c107 to your computer and use it in GitHub Desktop.
Save mshafae/2a5eaf94a0f7f81f725dc8e29872c107 to your computer and use it in GitHub Desktop.
Doubly linked list using C++ style shared and unique pointers.
// Gist https://gist.github.com/mshafae/2a5eaf94a0f7f81f725dc8e29872c107
// Filename list_with_cpp_ptr.cc
// CompileCommand clang++ -std=c++17 list_with_cpp_ptr.cc
// Doubly linked list using C++ style shared and unique pointers.
#include <iostream>
#include <memory>
class Printable {
public:
virtual void Print(std::ostream& out) = 0;
};
class Node : public Printable {
private:
int data_;
int id_;
std::shared_ptr<Node> prev_;
std::shared_ptr<Node> next_;
public:
Node(int data, int id, std::shared_ptr<Node> prev, std::shared_ptr<Node> next)
: data_{data}, id_{id}, prev_{prev}, next_{next} {
std::cerr << "Constructing: ";
Print(std::cerr);
std::cerr << "\n";
};
~Node() {
std::cerr << "\tDestroy: ";
Print(std::cerr);
std::cerr << "\n";
}
void Print(std::ostream& out) {
out << "Node(data = " << data_ << ", id = " << id_ << ", this = " << this
<< ", prev = " << prev_ << ", next = " << next_ << ")";
}
void Unlink() {
next_ = nullptr;
prev_ = nullptr;
}
void set_next(std::shared_ptr<Node> next) { next_ = next; }
void set_previous(std::shared_ptr<Node> prev) { prev_ = prev; }
std::shared_ptr<Node> next() { return next_; }
std::shared_ptr<Node> prev() { return prev_; }
int id() { return id_; }
};
class List : public Printable {
private:
std::shared_ptr<Node> front_;
std::shared_ptr<Node> back_;
int id_counter_;
public:
List() : front_{nullptr}, back_{nullptr}, id_counter_{0} {
std::cerr << "Constructing: ";
Print(std::cerr);
std::cerr << "\n";
}
~List() {
std::cerr << "Destroying:\n";
// List owns the nodes, so if it unlinks the nodes the reference count
// should decrease to zero thus marking the Node as delete-able.
std::shared_ptr<Node> cursor = front_;
while (cursor != nullptr) {
std::cerr << "\t~List: ";
cursor->Print(std::cerr);
std::cerr << "\n";
std::shared_ptr<Node> tmp_ptr = cursor;
cursor = cursor->next();
front_ = cursor;
tmp_ptr->Unlink();
}
std::cerr << "\n";
}
void PushFront(int data) {
id_counter_++;
if (front_ == nullptr && back_ == nullptr) {
// empty
std::shared_ptr<Node> new_node =
std::make_shared<Node>(data, id_counter_, nullptr, nullptr);
front_ = new_node;
back_ = new_node;
} else {
std::shared_ptr<Node> new_node =
std::make_shared<Node>(data, id_counter_, nullptr, front_);
front_->set_previous(new_node);
front_ = new_node;
}
}
void PushBack(int data) {
id_counter_++;
if (front_ == nullptr && back_ == nullptr) {
// empty
std::shared_ptr<Node> new_node =
std::make_shared<Node>(data, id_counter_, nullptr, nullptr);
front_ = new_node;
back_ = new_node;
} else {
std::shared_ptr<Node> new_node =
std::make_shared<Node>(data, id_counter_, back_, nullptr);
back_->set_next(new_node);
back_ = new_node;
}
}
void Print(std::ostream& out) {
std::shared_ptr<Node> cursor = front_;
out << "List contains:\n";
if (front_ == nullptr && back_ == nullptr) {
out << "\tempty\n";
return;
}
while (cursor != nullptr) {
out << "\t";
cursor->Print(std::cerr);
out << "\n";
cursor = cursor->next();
}
}
};
int main(int argc, char const* argv[]) {
std::unique_ptr<List> l = std::make_unique<List>();
for (int i = 0; i < 5; i++) {
l->PushFront(i + 10);
}
for (int i = 0; i < 5; i++) {
l->PushBack(i + 20);
}
std::cerr << "Printing l\n";
l->Print(std::cerr);
// Won't compile
// std::unique_ptr<List> m = l;
// Will compile because we're saying we want to move what's pointed by l to m.
std::unique_ptr<List> m = std::move(l);
// This will cause a segmentation fault.
// l->Print(std::cerr);
std::cerr << "Printing m\n";
m->Print(std::cerr);
return 0;
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment