All Notes
Implementing STL's std::unique_ptr
implementation probably okay by now. should probably look at doing write up. outline requirements, test cases, pseudocode, cpp code
impl
#pragma once
#include <utility>
#include <memory>
#include <type_traits>
template<typename T, typename Deleter = std::default_delete<T>>
class UniquePtr {
public:
UniquePtr() noexcept = default;
UniquePtr(T* ptr) noexcept : ptr_(ptr) {}
UniquePtr(T* ptr, Deleter deleter) noexcept : ptr_(ptr), deleter_(deleter) {}
UniquePtr(UniquePtr&& other) noexcept :
ptr_(std::exchange(other.ptr_, nullptr)),
deleter_(std::move(other.deleter_)) {}
~UniquePtr() {
if (ptr_) {
deleter_(ptr_);
}
}
UniquePtr& operator=(UniquePtr&& other) noexcept {
if (this != &other) {
if (ptr_) deleter_(ptr_);
ptr_ = std::exchange(other.ptr_, nullptr);
deleter_ = std::move(other.deleter_);
}
return *this;
}
UniquePtr(const UniquePtr&) = delete;
UniquePtr& operator=(const UniquePtr&) = delete;
T* get() const noexcept {
return ptr_;
}
T* release() noexcept {
T* temp = ptr_;
ptr_ = nullptr;
return temp;
}
void reset(T* ptr = nullptr) noexcept {
if (ptr_) deleter_(ptr_);
ptr_ = ptr;
}
void swap(UniquePtr& other) noexcept {
std::swap(ptr_, other.ptr_);
std::swap(deleter_, other.deleter_);
}
T& operator*() const noexcept {
return *ptr_;
}
T* operator->() const noexcept {
return ptr_;
}
explicit operator bool() const noexcept {
return ptr_ != nullptr;
}
bool operator==(const UniquePtr& other) const noexcept {
return ptr_ == other.ptr_;
}
bool operator!=(const UniquePtr& other) const noexcept {
return !(ptr_ == other.ptr_);
}
bool operator==(std::nullptr_t) const noexcept {
return ptr_ == nullptr;
}
bool operator!=(std::nullptr_t) const noexcept {
return !(ptr_ == nullptr);
}
// what to delete? the copy ctor and assign op
private:
T* ptr_ = nullptr;
Deleter deleter_{};
};
tests
#include <type_traits>
#include <gtest/gtest.h>
#include "unique_ptr/UniquePtr.h"
/* static asserts */
// • Not copy constructible - Cannot create copies via constructor
static_assert(!std::is_copy_constructible<UniquePtr<int>>::value);
// • Not copy assignable - Cannot create copies via assignment operator
static_assert(!std::is_copy_assignable<UniquePtr<int>>::value);
// • Move constructible - Can be constructed from rvalue
static_assert(std::is_move_constructible<UniquePtr<int>>::value);
// • Move assignable - Can be assigned from rvalue
static_assert(std::is_move_assignable<UniquePtr<int>>::value);
// could probably check for other things
// e.g. noexceptions in certain methods etc.
class UniquePtrTest : public ::testing::Test {
protected:
struct TestObject {
static int count;
TestObject() {count++;}
~TestObject() {count--;}
};
};
int UniquePtrTest::TestObject::count = 0;
/* construction */
TEST_F(UniquePtrTest, DefaultConstruction) {
UniquePtr<int> ptr;
EXPECT_EQ(ptr.get(), nullptr);
EXPECT_FALSE(ptr);
}
TEST_F(UniquePtrTest, PointerConstruction) {
auto* raw = new int(42);
UniquePtr<int> ptr(raw);
EXPECT_EQ(ptr.get(), raw);
EXPECT_TRUE(ptr);
EXPECT_EQ(*ptr, 42);
}
/* Move semantics */
TEST_F(UniquePtrTest, MoveConstruction) {
auto* raw = new int(42);
UniquePtr<int> ptr1(raw);
UniquePtr<int> ptr2 = std::move(ptr1);
EXPECT_EQ(ptr1.get(), nullptr);
EXPECT_EQ(ptr2.get(), raw);
EXPECT_EQ(*ptr2, 42);
}
TEST_F(UniquePtrTest, MoveAssignment) {
UniquePtr<int> ptr1(new int(42));
UniquePtr<int> ptr2(new int(100));
ptr2 = std::move(ptr1);
EXPECT_EQ(ptr1.get(), nullptr);
EXPECT_EQ(*ptr2, 42);
}
/* memory management */
TEST_F(UniquePtrTest, Reset) {
TestObject::count = 0;
UniquePtr<TestObject> ptr(new TestObject());
EXPECT_EQ(TestObject::count, 1);
ptr.reset();
EXPECT_EQ(TestObject::count, 0);
EXPECT_EQ(ptr.get(), nullptr);
}
TEST_F(UniquePtrTest, Release) {
auto* raw = new TestObject();
UniquePtr<TestObject> ptr(raw);
auto* released = ptr.release();
EXPECT_EQ(released, raw);
EXPECT_EQ(ptr.get(), nullptr);
delete released; // Manual cleanup
}
Other notes about STL Implementations and/or C++
- 🌲Part 1. Thread Pools
Thread pooling with c++20 primitives
- 🌲Part 2. Work Stealing Thread Pools
Work Stealing Thread Poools C++20
- 🌿Developing a deep learning framework
Developing a deep learning framework
- 🌱MPMC Queue
MPMC Queue
- 🌱C++ low-latency design patterns
A brief description of what this note covers
- 🌱Atomics
Atomics
- 🌿SPSC Queue
SPSC Thread-Safe Queue
- 🌿Implementing STL's std::shared_ptr
Implementing STL's std::shared_ptr
- 🌿Implementing STL's std::vector
A brief description of what this note covers
- 🌿Type Erasure in C++
Type Erasure in C++