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
36 changes: 36 additions & 0 deletions homework/CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
cmake_minimum_required(VERSION 3.10)
project(SharedPtrProject)

set(CMAKE_CXX_STANDARD 20)
set(CMAKE_CXX_STANDARD_REQUIRED ON)

#compile options
add_compile_options(-Wall -Wextra -Werror -pedantic)

#include header files
include_directories(${CMAKE_CURRENT_SOURCE_DIR})

#GTest include
include(FetchContent)
FetchContent_Declare(
googletest
GIT_REPOSITORY https://github.com/google/googletest.git
GIT_TAG release-1.12.1
)
FetchContent_MakeAvailable(googletest)

#Test executable
add_executable(shared_ptr_tests shared_ptr/shared_ptr_tests.cpp)
add_executable(weak_ptr_tests shared_ptr/weak_ptr_tests.cpp)
add_executable(make_shared_tests shared_ptr/make_shared_tests.cpp)

#Link GTest
target_link_libraries(shared_ptr_tests GTest::gtest GTest::gtest_main)
target_link_libraries(weak_ptr_tests GTest::gtest GTest::gtest_main)
target_link_libraries(make_shared_tests GTest::gtest GTest::gtest_main)

enable_testing()

add_test(NAME SharedPtrTests COMMAND shared_ptr_tests)
add_test(NAME WeakPtrTests COMMAND weak_ptr_tests)
add_test(NAME MakeSharedTests COMMAND make_shared_tests)
22 changes: 22 additions & 0 deletions homework/shared_ptr/make_shared.hpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
#pragma once
#include <utility>
#include "shared_ptr.hpp"

namespace my {

template <typename T, typename... Args>
shared_ptr<T> make_shared(Args&&... args) {
using CombinedBlock = typename shared_ptr<T>::ControlBlockWithObject;
auto* combined = new CombinedBlock(std::forward<Args>(args)...);

shared_ptr<T> result(&combined->object, &combined->control);
result.control_block->deleter = [](T* ptr) {
auto* combined_ptr = reinterpret_cast<CombinedBlock*>(
reinterpret_cast<char*>(ptr) - offsetof(CombinedBlock, object));
delete combined_ptr;
};

return result;
}

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

struct TestStruct {
int x;
std::string str;

TestStruct(int x_, std::string str_)
: x(x_), str(std::move(str_)) {}
};

TEST(MakeSharedTest, BasicType) {
auto sp = my::make_shared<int>(42);
EXPECT_EQ(*sp, 42);
EXPECT_EQ(sp.use_count(), 1);
}

TEST(MakeSharedTest, CustomType) {
auto sp = my::make_shared<TestStruct>(42, "test");
EXPECT_EQ(sp->x, 42);
EXPECT_EQ(sp->str, "test");
EXPECT_EQ(sp.use_count(), 1);
}

TEST(MakeSharedTest, WeakPtr) {
auto sp = my::make_shared<int>(42);
my::weak_ptr<int> wp(sp);

EXPECT_EQ(wp.use_count(), 1);
EXPECT_FALSE(wp.expired());

auto locked = wp.lock();
EXPECT_EQ(*locked, 42);
}
127 changes: 127 additions & 0 deletions homework/shared_ptr/shared_ptr.hpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,127 @@
#pragma once
#include <atomic>
#include <utility>

namespace my {

template <typename T>
class weak_ptr; // Forward declaration
template <typename T>
class shared_ptr; // Forward declaration
template <typename T, typename... Args>
shared_ptr<T> make_shared(Args&&... args);

template <typename T>
class shared_ptr {
private:
T* ptr;

struct ControlBlock {
std::atomic<size_t> shared_count;
std::atomic<size_t> weak_count;
void (*deleter)(T*);

ControlBlock()
: shared_count(1), weak_count(0), deleter(nullptr) {}
};

struct ControlBlockWithObject {
ControlBlock control;
T object;

template <typename... Args>
ControlBlockWithObject(Args&&... args)
: control(), object(std::forward<Args>(args)...) {}
};

ControlBlock* control_block;

// Private constructor for weak_ptr::lock() and make_shared
shared_ptr(T* p, ControlBlock* cb) noexcept
: ptr(p), control_block(cb) {}

friend class weak_ptr<T>;
template <typename U, typename... Args>
friend shared_ptr<U> make_shared(Args&&... args);

public:
explicit shared_ptr(T* p = nullptr)
: ptr(p), control_block(new ControlBlock()) {}

shared_ptr(const shared_ptr& other)
: ptr(other.ptr), control_block(other.control_block) {
if (control_block) {
++control_block->shared_count;
}
}

shared_ptr& operator=(const shared_ptr& otherSharedPtr) {
shared_ptr(otherSharedPtr).swap(*this);
return *this;
}

shared_ptr(shared_ptr&& otherSharedPtr) noexcept
: ptr(otherSharedPtr.ptr), control_block(otherSharedPtr.control_block) {
otherSharedPtr.ptr = nullptr;
otherSharedPtr.control_block = nullptr;
}

shared_ptr& operator=(shared_ptr&& otherSharedPtr) noexcept {
shared_ptr(std::move(otherSharedPtr)).swap(*this);
return *this;
}

~shared_ptr() {
if (control_block) {
if (--control_block->shared_count == 0) {
if (control_block->deleter) {
control_block->deleter(ptr);
control_block = nullptr;
ptr = nullptr;
} else {
delete ptr;
if (control_block->weak_count == 0) {
delete control_block;
}
control_block = nullptr;
ptr = nullptr;
}
}
}
}

T& operator*() const { return *ptr; }
T* operator->() const { return ptr; }
T* get() const { return ptr; }

size_t use_count() const {
return (control_block ? control_block->shared_count.load() : 0);
}

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

void swap(shared_ptr& otherSharedPtr) noexcept {
std::swap(ptr, otherSharedPtr.ptr);
std::swap(control_block, otherSharedPtr.control_block);
}

void reset(T* p = nullptr) {
if (p != ptr) {
if (control_block) {
if (--control_block->shared_count == 0) {
delete ptr;
if (control_block->weak_count == 0) {
delete control_block;
}
}
}
ptr = p;
if (p) {
control_block = new ControlBlock();
} else {
control_block = nullptr;
}
}
}
};
} // namespace my
131 changes: 131 additions & 0 deletions homework/shared_ptr/shared_ptr_tests.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,131 @@
#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, Swap) {
my::shared_ptr<int> sp1(new int(42));
my::shared_ptr<int> sp2(new int(24));
sp1.swap(sp2);
EXPECT_EQ(*sp1, 24);
EXPECT_EQ(*sp2, 42);
}

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
Loading