diff --git a/homework/unique_ptr/unique_ptr.hpp b/homework/unique_ptr/unique_ptr.hpp new file mode 100644 index 00000000..7a093a97 --- /dev/null +++ b/homework/unique_ptr/unique_ptr.hpp @@ -0,0 +1,77 @@ +#pragma once +#include + +namespace my { + +template +class unique_ptr { +public: + // Constructors + constexpr unique_ptr() noexcept { + ptr_ = nullptr; + } + constexpr explicit unique_ptr(std::nullptr_t) noexcept { + ptr_ = nullptr; + } + explicit unique_ptr(T* ptr) noexcept + : ptr_(ptr) { + } + + // Copy constructor + unique_ptr(const unique_ptr&) = delete; + + // Copy assignment + unique_ptr& operator=(const unique_ptr&) = delete; + + // Move constructor + unique_ptr(unique_ptr&& new_ptr) noexcept { + ptr_ = new_ptr.ptr_; + new_ptr.ptr_ = nullptr; + } + + // Move assignment + unique_ptr& operator=(unique_ptr&& new_ptr) noexcept { + if (this != &new_ptr) { + delete ptr_; + ptr_ = new_ptr.ptr_; + new_ptr.ptr_ = nullptr; + } + return *this; + } + + // Destructor + ~unique_ptr() { + delete ptr_; + } + + // Methods + T& operator*() const noexcept { + return *ptr_; + } + + T* operator->() const noexcept { + return ptr_; + } + + T* get() const noexcept { + return ptr_; + } + + T* release() noexcept { + T* tmp = ptr_; + ptr_ = nullptr; + return tmp; + } + + void reset(T* ptr = nullptr) noexcept { + if (ptr_ != ptr) { + delete ptr_; + ptr_ = ptr; + } + } + +private: + T* ptr_; +}; + +} // namespace my diff --git a/homework/unique_ptr/unique_ptr_tests.cpp b/homework/unique_ptr/unique_ptr_tests.cpp new file mode 100644 index 00000000..d5003202 --- /dev/null +++ b/homework/unique_ptr/unique_ptr_tests.cpp @@ -0,0 +1,152 @@ +// ReSharper disable CppUseAuto +#include "unique_ptr.hpp" + +#include +#include + +#include + +template +using unique_ptr = my::unique_ptr; +// Alternative for all possible cases: +// using namespace my; + +unique_ptr mock_ptr; + +TEST_CASE("Create unique ptr", "[unique_ptr]") { + SECTION("Integer") { + SECTION("Empty unique ptr") { + const unique_ptr empty_ptr; + CHECK(empty_ptr.get() == nullptr); + } + + SECTION("Nullptr unique ptr") { + const unique_ptr null_ptr{nullptr}; + CHECK(null_ptr.get() == nullptr); + } + + SECTION("Raw data unique ptr") { + const unique_ptr init_ptr{new int(1)}; + CHECK(init_ptr.get() != nullptr); + + int* raw_ptr = new int(1); + const unique_ptr ptr{raw_ptr}; + CHECK(ptr.get() == raw_ptr); + } + } + + SECTION("Double empty unique ptr") { + const unique_ptr empty_ptr; + CHECK(empty_ptr.get() == nullptr); + } +} + +TEST_CASE("Destroy unique ptr", "[unique_ptr]") { + SKIP("Checking releasing memory of pointer required custom deleter or system signal handling"); + + GIVEN("Integer unique ptr") { + int* raw_ptr = new int(1); + WHEN("Releasing memory of raw_ptr by creating and destroying unique ptr") { + { + const unique_ptr ptr{raw_ptr}; + REQUIRE(ptr.get() == raw_ptr); + } + THEN("Releasing memory of raw_ptr, once again") { + REQUIRE_THROWS(delete raw_ptr); + } + } + } +} + +TEST_CASE("Copy unique ptr", "[unique_ptr]") { + SECTION("Copy constructor should be deleted") { + CHECK(!std::is_copy_constructible_v>); + } + + SECTION("Copy assignment should be deleted") { + CHECK(!std::is_copy_assignable_v>); + } +} + +TEST_CASE("Move unique ptr", "[unique_ptr]") { + constexpr int assignedValue = 10; + + SECTION("Move constructor") { + REQUIRE(std::is_move_constructible_v>); + + unique_ptr ptr{new int(1)}; + *ptr = assignedValue; + const unique_ptr ptr2{std::move(ptr)}; + + CHECK(ptr.get() == nullptr); + CHECK(*ptr2 == assignedValue); + } + + SECTION("Move assignment") { + REQUIRE(std::is_move_assignable_v>); + + unique_ptr ptr{new int(1)}; + *ptr = assignedValue; + unique_ptr ptr2{new int(1)}; + ptr2 = std::move(ptr); + ptr2 = std::move(ptr2); + + CHECK(ptr.get() == nullptr); + CHECK(*ptr2.get() == assignedValue); + } +} + +struct ValueTest { + int value; + + [[nodiscard]] int get_value() const noexcept { + return value; + } + + void set_value(const int new_value) noexcept { + this->value = new_value; + } +}; + +TEST_CASE("Member functions", "[unique_ptr]") { + SECTION("operator*()") { + const unique_ptr ptr{new int(1)}; + *ptr = 10; + REQUIRE(*ptr == 10); + *ptr = 20; + REQUIRE(*ptr == 20); + } + + SECTION("operator->") { + const unique_ptr ptr{new ValueTest{10}}; + ptr->set_value(20); + REQUIRE(ptr->get_value() == 20); + ptr->set_value(-30); + REQUIRE(ptr->get_value() == -30); + } + + SECTION("get()") { + const unique_ptr ptr{new ValueTest{10}}; + ValueTest* value_test = ptr.get(); + REQUIRE(value_test->value == 10); + } + + SECTION("release()") { + unique_ptr ptr{new ValueTest{10}}; + ValueTest* value_ptr = ptr.release(); + delete value_ptr; + REQUIRE(ptr.get() == nullptr); + } + + SECTION("reset()") { + unique_ptr ptr{new ValueTest{10}}; + ValueTest* new_value = new ValueTest{20}; + ptr.reset(nullptr); + ptr.reset(nullptr); + ptr.reset(nullptr); + ptr.reset(new_value); + ptr.reset(new_value); + ptr.reset(new_value); + REQUIRE(ptr.get() == new_value); + } +}