Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
24 changes: 24 additions & 0 deletions homework/shared_ptr/CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
cmake_minimum_required(VERSION 3.11.0)
project(shared_ptr)

set(CMAKE_CXX_STANDARD 17)
set(CMAKE_CXX_STANDARD_REQUIRED ON)

include(FetchContent)
FetchContent_Declare(
googletest
GIT_REPOSITORY https://github.com/google/googletest.git
GIT_TAG main
)
# For Windows: Prevent overriding the parent project's compiler/linker settings
set(gtest_force_shared_crt ON CACHE BOOL "" FORCE)
FetchContent_MakeAvailable(googletest)

enable_testing()

add_executable(${PROJECT_NAME}_tests ${PROJECT_NAME}_tests.cpp)

target_link_libraries(${PROJECT_NAME}_tests gtest_main)

include(GoogleTest)
gtest_discover_tests(${PROJECT_NAME}_tests)
10 changes: 10 additions & 0 deletions homework/shared_ptr/build.bat
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
RMDIR build /S /Q
mkdir build
cd build
cmake -G "MinGW Makefiles" ..
MinGW32-make

shared_ptr_tests.exe

cd ..

1 change: 1 addition & 0 deletions homework/shared_ptr/format.bat
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
clang-format --style=file -i *.hpp *.cpp
123 changes: 123 additions & 0 deletions homework/shared_ptr/shared_ptr.hpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,123 @@
#pragma once

namespace my {

template <typename T>
class shared_ptr {
private:
// Holds a pointer to managed object (template class)
T* obj_ptr;
// Holds a pointer to shared control block with 2 counters and a deleter:
// * shared_refs count (as `std::atomic<size_t>`)
// * weak_refs count (as `std::atomic<size_t>`)
// * deleter (function pointer)
struct ControlBlock {
std::atomic<size_t> shared_refs;
std::atomic<size_t> weak_refs;
void (*deleter)(T*);

ControlBlock()
: shared_refs(1), weak_refs(0), deleter(nullptr) {}
};
ControlBlock* control_block_ptr;

void remove_this_pointer() {
if (control_block_ptr) {
control_block_ptr->shared_refs--;
if (0 == control_block_ptr->shared_refs) {
if (control_block_ptr->deleter) {
control_block_ptr->deleter(obj_ptr);
} else {
delete obj_ptr;
}
if (0 == control_block_ptr->weak_refs) {
delete control_block_ptr;
}
}
}
}

public:
// Constructor: copies a pointer and allocate the control block
shared_ptr(T* pointer = nullptr) {
obj_ptr = pointer;
control_block_ptr = new ControlBlock();
}
// Destructor: decrease shared_refs and:
// - if shared_refs == 0 -> release the managed object
// - if shared_refs == 0 and weak_refs == 0 -> release the control block
~shared_ptr() {
remove_this_pointer();
}
// Copying is allowed - it increments shared_refs
shared_ptr(const shared_ptr& other) {
obj_ptr = other.obj_ptr;
control_block_ptr = other.control_block_ptr;
other.control_block_ptr->shared_refs++;
}
shared_ptr& operator=(const shared_ptr& other) {
remove_this_pointer();
obj_ptr = other.obj_ptr;
control_block_ptr = other.control_block_ptr;
if (control_block_ptr)
control_block_ptr->shared_refs++;
return *this;
}
// Moving is allowed and it means:
// * Copying original pointers to a new object
// * Setting source pointer to nullptr
shared_ptr(shared_ptr&& other)
: obj_ptr(other.obj_ptr), control_block_ptr(other.control_block_ptr) {
other.obj_ptr = nullptr;
other.control_block_ptr = nullptr;
}
shared_ptr& operator=(shared_ptr&& other) {
remove_this_pointer();

obj_ptr = other.obj_ptr;
control_block_ptr = other.control_block_ptr;

other.obj_ptr = nullptr;
other.control_block_ptr = nullptr;

return *this;
}

size_t use_count() {
if (control_block_ptr) {
return control_block_ptr->shared_refs;
} else {
return 0;
}
}

T& operator*() {
return *obj_ptr;
}

T* operator->() {
return obj_ptr;
}

explicit operator bool() const {
return obj_ptr != nullptr;
}

T* get() {
return obj_ptr;
}

void reset(T* new_ptr = nullptr) {
if (obj_ptr != new_ptr) {
remove_this_pointer();
obj_ptr = new_ptr;
if (new_ptr) {
control_block_ptr = new ControlBlock();
} else {
control_block_ptr = nullptr;
}
}
}
};

} // namespace my
124 changes: 124 additions & 0 deletions homework/shared_ptr/shared_ptr_tests.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,124 @@
#include <gtest/gtest.h>
#include "shared_ptr.hpp"

// Instantiate the template class for testing
template class my::shared_ptr<int>;

TEST(SharedPtrTest, DefaultConstructor) {
my::shared_ptr<int> sp;
EXPECT_EQ(sp.use_count(), 1);
EXPECT_FALSE(sp);
}

TEST(SharedPtrTest, ConstructorWithPointer) {
my::shared_ptr<int> sp(new int(42));
EXPECT_EQ(sp.use_count(), 1);
EXPECT_TRUE(sp);
EXPECT_EQ(*sp, 42);
}

TEST(SharedPtrTest, ArrowOperator) {
struct TestStruct {
int value;
TestStruct(int v)
: value(v) {}
};

my::shared_ptr<TestStruct> sp(new TestStruct(42));
EXPECT_EQ(sp->value, 42);
sp->value = 24;
EXPECT_EQ(sp->value, 24);
}

TEST(SharedPtrTest, GetMethod) {
int* raw_ptr = new int(10);
my::shared_ptr<int> sp(raw_ptr);

EXPECT_EQ(sp.get(), raw_ptr);
EXPECT_EQ(*sp.get(), 10);

EXPECT_EQ(sp.use_count(), 1);
}

TEST(SharedPtrTest, ResetMethod) {
my::shared_ptr<int> sp(new int(5));
EXPECT_EQ(*sp, 5);
EXPECT_EQ(sp.use_count(), 1);

sp.reset(new int(10));
EXPECT_EQ(*sp, 10);
EXPECT_EQ(sp.use_count(), 1);

sp.reset();
EXPECT_EQ(sp.get(), nullptr);
EXPECT_EQ(sp.use_count(), 0);
}

TEST(SharedPtrTest, ResetWithMultipleReferences) {
my::shared_ptr<int> sp1(new int(5));
my::shared_ptr<int> sp2 = sp1;

EXPECT_EQ(sp1.use_count(), 2);
EXPECT_EQ(sp2.use_count(), 2);

sp1.reset(new int(10));
EXPECT_EQ(*sp1, 10);
EXPECT_EQ(sp1.use_count(), 1);
EXPECT_EQ(*sp2, 5);
EXPECT_EQ(sp2.use_count(), 1);
}

TEST(SharedPtrTest, CopyConstructor) {
my::shared_ptr<int> sp1(new int(42));
my::shared_ptr<int> sp2(sp1);
EXPECT_EQ(*sp1, 42);
EXPECT_EQ(*sp2, 42);
EXPECT_EQ(sp1.use_count(), 2);
EXPECT_EQ(sp2.use_count(), 2);
}

TEST(SharedPtrTest, CopyAssignment) {
my::shared_ptr<int> sp1(new int(42));
my::shared_ptr<int> sp2;
sp2 = sp1;
EXPECT_EQ(*sp1, 42);
EXPECT_EQ(*sp2, 42);
EXPECT_EQ(sp1.use_count(), 2);
EXPECT_EQ(sp2.use_count(), 2);
}

TEST(SharedPtrTest, MoveConstructor) {
my::shared_ptr<int> sp1(new int(42));
my::shared_ptr<int> sp2(std::move(sp1));
EXPECT_EQ(*sp2, 42);
EXPECT_EQ(sp2.use_count(), 1);
EXPECT_FALSE(sp1);
}

TEST(SharedPtrTest, MoveAssignment) {
my::shared_ptr<int> sp1(new int(42));
my::shared_ptr<int> sp2;
sp2 = std::move(sp1);
EXPECT_EQ(*sp2, 42);
EXPECT_EQ(sp2.use_count(), 1);
EXPECT_FALSE(sp1);
}

TEST(SharedPtrTest, BoolOperator) {
my::shared_ptr<int> sp1(new int(42));
my::shared_ptr<int> sp2;
EXPECT_TRUE(sp1);
EXPECT_FALSE(sp2);
}

TEST(SharedPtrTest, MultipleSharedPtrs) {
my::shared_ptr<int> sp1(new int(42));
{
my::shared_ptr<int> sp2 = sp1;
my::shared_ptr<int> sp3 = sp1;
EXPECT_EQ(sp1.use_count(), 3);
EXPECT_EQ(sp2.use_count(), 3);
EXPECT_EQ(sp3.use_count(), 3);
}
EXPECT_EQ(sp1.use_count(), 1);
}
Loading