Parent directory
Makefile
sharedptr.h
sharedptr1.cpp
sharedptr2.cpp
uniqueptr.cpp
CC = g++
CXX = g++
CFLAGS = -g -Wall
CXXFLAGS = -g -Wall -std=c++20
# turn off warnings about unused variables
CXXFLAGS += -Wno-unused-variable
executables = sharedptr1 sharedptr2 uniqueptr
.PHONY: default
default: $(executables)
sharedptr1: sharedptr1.o
sharedptr2: sharedptr2.o
uniqueptr: uniqueptr.o
sharedptr1.o: sharedptr.h
sharedptr2.o: sharedptr.h
.PHONY: clean
clean:
rm -f *~ a.out core *.o $(executables)
.PHONY: all
all: clean defaulttemplate <typename T>
class SharedPtr {
T* ptr; // the underlying pointer
int* count; // the reference count
public:
explicit SharedPtr(T* p = nullptr) : ptr{p}, count{new int{1}} {}
~SharedPtr() {
if (--*count == 0) {
delete count;
delete ptr;
}
}
SharedPtr(const SharedPtr<T>& sp) : ptr(sp.ptr), count(sp.count) {
++*count;
}
SharedPtr<T>& operator=(const SharedPtr<T>& sp) {
if (this != &sp) {
// first, detach.
if (--*count == 0) {
delete count;
delete ptr;
}
// attach to the new object.
ptr = sp.ptr;
count = sp.count;
++*count;
}
return *this;
}
T& operator*() const { return *ptr; }
T* operator->() const { return ptr; }
operator void*() const { return ptr; } // enables "if (sp) ..."
T* get_ptr() const { return ptr; } // access to underlying ptr
int get_count() const { return *count; } // access to reference count
};#include <string>
#include <vector>
#include <iostream>
#include <iomanip>
#include <utility>
#include <memory>
#include "sharedptr.h"
using namespace std;
auto create_vec1() {
// SharedPtr<T> replaces T* for heap-allocated object.
// That is, instead of doing
//
// string* p { new string{"hello"} };
//
// We do the following:
SharedPtr<string> p1 { new string{"hello"} };
SharedPtr<string> p2 { new string{"c"} };
SharedPtr<string> p3 { new string{"students"} };
vector v { p1, p2, p3 };
return v;
}
template <typename T, typename... Args>
SharedPtr<T> MakeSharedPtr(Args&&... args) {
return SharedPtr<T>{new T{forward<Args>(args)...}};
}
auto create_vec2() {
auto p1 { MakeSharedPtr<string>("hello") };
auto p2 { MakeSharedPtr<string>("c") };
auto p3 { MakeSharedPtr<string>("students") };
vector v { p1, p2, p3 };
return v;
}
auto create_vec3() {
auto p1 { make_shared<string>("hello") };
auto p2 { make_shared<string>("c") };
auto p3 { make_shared<string>("students") };
static_assert(is_same_v<decltype(p3), shared_ptr<string>>);
vector v { p1, p2, p3 };
return v;
}
int main() {
vector v1 = create_vec1();
// vector v1 = create_vec2();
// vector v1 = create_vec3();
for (auto p : v1) { cout << *p + " "; } cout << '\n';
// for (auto& p : v1) { cout << *p + " "; } cout << '\n';
// for (const auto& p : v1) { cout << *p + " "; } cout << '\n';
// for (auto&& p : v1) { cout << *p + " "; } cout << '\n';
vector v2 = v1;
*v2[1] = "c2cpp";
*v2[2] = "hackers";
// Print v1 again
for (auto&& p : v1) { cout << *p + " "; } cout << '\n';
cout << "\nReference count demo:\n";
{
SharedPtr<string> p1 { new string{"hello"} };
cout << "before foo(), reference count: " << p1.get_count() << '\n';
auto foo = [](SharedPtr<string> p) {
cout << "inside foo(), reference count: " << p.get_count() << '\n';
};
foo(p1);
cout << "after foo(), reference count: " << p1.get_count() << '\n';
}
}#include <string>
#include <vector>
#include <iostream>
#include <iomanip>
#include <utility>
#include <memory>
#include <cassert>
#include <atomic>
#include "sharedptr.h"
using namespace std;
int main() {
// 1. make_shared<T>() combines heap allocations
{
// string* p0 { new string(50, 'A') };
// shared_ptr<string> p1 { new string(50, 'B') };
// shared_ptr<string> p2 { make_shared<string>(50, 'C') };
}
// 2. shared_ptr to an array and custom deleter
{
// Invalid delete
// SharedPtr<string> p0 { new string[2] {"hello", "c2cpp"} };
// cout << *p0 << ' ' << p0.get_ptr()[1] << '\n';
// Invalid delete
// shared_ptr<string> p1 { new string[2] {"hello", "c2cpp"} };
// cout << *p1 << ' ' << p1.get()[1] << '\n';
// Custom deleter
// shared_ptr<string> p2 { new string[2] {"hello", "c2cpp"},
// [](string* s) { delete[] s; }
// };
// cout << *p2 << ' ' << p2.get()[1] << '\n';
// shared_ptr parameterized with array type
// shared_ptr<string[]> p3 { new string[2] {"hello", "c2cpp"} };
// cout << p3[0] << " " << p3[1] << '\n';
}
// 3. Aliasing
{
// shared_ptr<pair<string,int>> p1 { new pair{"hi"s, 5} };
// shared_ptr<int> p2 { p1, &p1->second };
// cout << p1->first << ' ' << *p2 << '\n';
// assert(p1.use_count() == 2 && p2.use_count() == 2);
}
// 4. weak_ptr
{
// auto weak_test = [](weak_ptr<string> wp) {
// shared_ptr<string> sp = wp.lock();
// if (sp) {
// assert(sp.use_count() == 2);
// cout << *sp << '\n';
// } else {
// cout << "weak_ptr expired\n";
// }
// };
// weak_ptr<string> wp;
// {
// auto sp = make_shared<string>("hello");
// assert(sp.use_count() == 1);
// wp = sp; // weak_ptr does not increase
// assert(sp.use_count() == 1); // the shared_ptr's ref count
// weak_test(wp); // sp is alive, so is wp
// }
// weak_test(wp); // sp is gone, so wp is expired
}
}#include <string>
#include <vector>
#include <iostream>
#include <iomanip>
#include <utility>
#include <memory>
#include <cassert>
using namespace std;
unique_ptr<string[]> create_array(size_t n, string s) {
auto up = make_unique<string[]>(n);
for (size_t i = 0; i < n; ++i) {
up[i] = s;
}
return up;
}
int main() {
size_t n = 1000;
unique_ptr<string[]> up1 = create_array(n, "hello");
// unique_ptr holds just a pointer
static_assert(sizeof(up1) == 8);
for (size_t i = 0; i < n; ++i) { assert(up1[i] == "hello"s); }
unique_ptr<string[]> up2;
assert(!up2);
// This doesn't compile -- unique_ptr cannot be copied
// up2 = up1;
// But it can be moved
up2 = std::move(up1);
assert(!up1 && up2);
// It can also be moved to a shared_ptr
shared_ptr<string[]> sp { std::move(up2) };
assert(!up2 && sp.use_count() == 1);
for (size_t i = 0; i < n; ++i) { assert(sp[i] == "hello"s); }
}