#include // assert #include // std::size_t #include // std::swap and std::forward template class Counted_Pointer { public: Counted_Pointer() = default; template Counted_Pointer(Args&&... args) : block{ new Counted_Block{ T { std::forward(args)... }, 1 } } { } Counted_Pointer(Counted_Pointer& other) : Counted_Pointer { const_cast(other) } { } Counted_Pointer(Counted_Pointer const& other) : block { other.block } { if (block) { ++block->count; } } Counted_Pointer(Counted_Pointer&& other) : block { other.block } { other.block = nullptr; } ~Counted_Pointer() { if (block) { --block->count; if (block->count == 0) { delete block; } } } Counted_Pointer& operator=(Counted_Pointer const& other) { return *this = Counted_Pointer{ other }; } Counted_Pointer& operator=(Counted_Pointer&& other) { std::swap(other.block, block); return *this; } T& operator*() { return block->data; } T const& operator*() const { return block->data; } T* operator->() { return &(block->data); } T const* operator->() const { return &(block->data); } std::size_t count() const { if (block) { return block->count; } return 0; } private: struct Counted_Block { T data; std::size_t count; }; Counted_Block* block { nullptr }; }; template Counted_Pointer make_counted(Args&&... args) { return { std::forward(args)... }; } // Start of testcases (no modification should be needed beyond this point) struct X { int a; int b; }; Counted_Pointer test(Counted_Pointer ptr) { return ptr; } int main() { // Create a counted_pointer to an X object auto ptr1 = make_counted(3, 5); // Check that the data and count is correct assert(ptr1->a == 3); assert(ptr1->b == 5); assert(ptr1.count() == 1); { // Create a const copy and check that the count is increased // This will call the non-const overload of the copy constructor Counted_Pointer const cpy1 = ptr1; assert(ptr1.count() == 2); assert(cpy1.count() == 2); // Check that the data is the same assert(cpy1->a == 3); assert(cpy1->b == 5); // Modify the original pointer... ++ptr1->a; ++ptr1->b; // ... and check that the change occurs in cpy1 assert(cpy1->a == 4); assert(cpy1->b == 6); // Make another copy to check that both copy constructors works // This will call the const overload of the copy constructor { auto cpy2 = cpy1; assert(ptr1.count() == 3); assert(cpy1.count() == 3); assert(cpy2.count() == 3); } assert(ptr1.count() == 2); } // Create an empty pointer Counted_Pointer other { }; assert(other.count() == 0); // Test the move assignment other = std::move(ptr1); assert(ptr1.count() == 0); assert(other.count() == 1); assert(other->a == 4); assert(other->b == 6); { // Test the move constructor auto ptr2 = test(Counted_Pointer{8}); assert(ptr2.count() == 1); assert(*ptr2 == 8); // Try the const version as well Counted_Pointer const ptr3 = ptr2; assert(ptr3.count() == 2); assert(*ptr3 == 8); *ptr2 = 1; assert(*ptr3 == 1); } // Check with the terminal command: // valgrind ./a.out // That no memory is leaked. }